From 6d815b64db2192826429e2c8c6f4aa1689435323 Mon Sep 17 00:00:00 2001 From: kukulaka Date: Mon, 21 Sep 2020 16:56:14 +0100 Subject: [PATCH 001/366] amended collections def so field length is 64 chars --- api/src/database/seeds/01-tables/01-collections.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/api/src/database/seeds/01-tables/01-collections.yaml b/api/src/database/seeds/01-tables/01-collections.yaml index 86ee4d7061..9f8a998143 100644 --- a/api/src/database/seeds/01-tables/01-collections.yaml +++ b/api/src/database/seeds/01-tables/01-collections.yaml @@ -3,6 +3,7 @@ table: directus_collections columns: collection: type: string + length: 64 primary: true icon: type: string From e6f7820614840a6cc83a44aaa1d253f4a3baf53c Mon Sep 17 00:00:00 2001 From: Ben Haynes Date: Mon, 21 Sep 2020 12:08:48 -0400 Subject: [PATCH 002/366] Fix typo on roels --- docs/concepts/users-roles-and-permissions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/concepts/users-roles-and-permissions.md b/docs/concepts/users-roles-and-permissions.md index 4ca58e593b..9ca75a59fa 100644 --- a/docs/concepts/users-roles-and-permissions.md +++ b/docs/concepts/users-roles-and-permissions.md @@ -30,7 +30,7 @@ Each user is assigned to one Role, which determines their permissions within the ### Public Role -Public is not technically a role, and can't be found in the `directus_roels` table. Instead, it represents the _lack_ of a role, providing a place to configure permissions for unauthenticated users. This role can not be deleted, and has no permissions by default. +Public is not technically a role, and can't be found in the `directus_roles` table. Instead, it represents the _lack_ of a role, providing a place to configure permissions for unauthenticated users. This role can not be deleted, and has no permissions by default. ### Administrators Role From b009fa86b152c972117c06133d02cbbeb6c11188 Mon Sep 17 00:00:00 2001 From: rijkvanzanten Date: Mon, 21 Sep 2020 12:19:58 -0400 Subject: [PATCH 003/366] Remove misc divider from settings Fixes #378 --- api/src/database/seeds/03-fields/09-settings.yaml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/api/src/database/seeds/03-fields/09-settings.yaml b/api/src/database/seeds/03-fields/09-settings.yaml index 88a561b326..fd9c776601 100644 --- a/api/src/database/seeds/03-fields/09-settings.yaml +++ b/api/src/database/seeds/03-fields/09-settings.yaml @@ -199,17 +199,6 @@ fields: text: Presets Only sort: 14 width: half - - collection: directus_settings - field: misc_divider - interface: divider - locked: true - options: - icon: pending - title: Miscellaneous - color: '#2F80ED' - special: alias - sort: 15 - width: full - collection: directus_settings field: id hidden: true From adb786ed910e450f7d458197de0a9882b2e3d35b Mon Sep 17 00:00:00 2001 From: rijkvanzanten Date: Mon, 21 Sep 2020 12:27:36 -0400 Subject: [PATCH 004/366] Only show notification text on errors Fixes #377 --- app/src/composables/use-item/use-item.ts | 40 ++++++------------------ app/src/stores/collections.ts | 2 -- app/src/stores/settings.ts | 1 - 3 files changed, 10 insertions(+), 33 deletions(-) diff --git a/app/src/composables/use-item/use-item.ts b/app/src/composables/use-item/use-item.ts index 00b26a94c6..a9423b2ee6 100644 --- a/app/src/composables/use-item/use-item.ts +++ b/app/src/composables/use-item/use-item.ts @@ -93,12 +93,6 @@ export function useItem(collection: Ref, primaryKey: Ref, primaryKey: Ref, primaryKey: Ref err.extensions.code === 'FAILED_VALIDATION').map((err: APIError) => { - return err.extensions; - }); + validationErrors.value = err.response.data.errors + .filter((err: APIError) => err.extensions.code === 'FAILED_VALIDATION') + .map((err: APIError) => { + return err.extensions; + }); } else { throw err; } @@ -175,10 +165,6 @@ export function useItem(collection: Ref, primaryKey: Ref, primaryKey: Ref err.extensions.code === 'FAILED_VALIDATION').map((err: APIError) => { - return err.extensions; - }); + validationErrors.value = err.response.data.errors + .filter((err: APIError) => err.extensions.code === 'FAILED_VALIDATION') + .map((err: APIError) => { + return err.extensions; + }); } else { throw err; } @@ -237,10 +225,6 @@ export function useItem(collection: Ref, primaryKey: Ref, primaryKey: Ref Date: Mon, 21 Sep 2020 12:51:50 -0400 Subject: [PATCH 005/366] Show hidden on detail instead of "hidden" on field select Ref #375 --- app/src/lang/en-US/index.json | 2 ++ .../routes/data-model/fields/components/field-select.vue | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/lang/en-US/index.json b/app/src/lang/en-US/index.json index 5da1b874d0..792601ce45 100644 --- a/app/src/lang/en-US/index.json +++ b/app/src/lang/en-US/index.json @@ -162,6 +162,8 @@ "hidden_fields": "Hidden Fields", "hidden_field": "Hidden Field", + "hidden_on_detail": "Hidden on Detail", + "key": "Key", "alias": "Alias", diff --git a/app/src/modules/settings/routes/data-model/fields/components/field-select.vue b/app/src/modules/settings/routes/data-model/fields/components/field-select.vue index 55be5d5d22..80246648fc 100644 --- a/app/src/modules/settings/routes/data-model/fields/components/field-select.vue +++ b/app/src/modules/settings/routes/data-model/fields/components/field-select.vue @@ -91,7 +91,7 @@ From e4a25ffd1a5a287263df3c1fce891ef074255f75 Mon Sep 17 00:00:00 2001 From: rijkvanzanten Date: Mon, 21 Sep 2020 12:56:48 -0400 Subject: [PATCH 006/366] Show all fields on layouts Fixes #375 --- app/src/layouts/cards/cards.vue | 11 ++++------- app/src/layouts/tabular/tabular.vue | 14 +++++--------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/app/src/layouts/cards/cards.vue b/app/src/layouts/cards/cards.vue index 04b573207a..42cfebd293 100644 --- a/app/src/layouts/cards/cards.vue +++ b/app/src/layouts/cards/cards.vue @@ -57,7 +57,7 @@ @@ -102,7 +56,7 @@ import formatFilesize from '@/utils/format-filesize'; import i18n from '@/lang'; import FileLightbox from '@/views/private/components/file-lightbox'; import ImageEditor from '@/views/private/components/image-editor'; -import ModalBrowse from '@/views/private/components/modal-browse'; + import { nanoid } from 'nanoid'; import getRootPath from '@/utils/get-root-path'; @@ -116,7 +70,7 @@ type Image = { }; export default defineComponent({ - components: { FileLightbox, ImageEditor, ModalBrowse }, + components: { FileLightbox, ImageEditor }, props: { value: { type: String, @@ -128,7 +82,6 @@ export default defineComponent({ }, }, setup(props, { emit }) { - const activeDialog = ref<'choose' | 'url' | null>(null); const loading = ref(false); const image = ref(null); const error = ref(null); @@ -180,8 +133,6 @@ export default defineComponent({ } ); - const { url, isValidURL, loading: urlLoading, error: urlError, importFromURL } = useURLImport(); - return { loading, image, @@ -194,12 +145,6 @@ export default defineComponent({ setImage, deselect, downloadSrc, - activeDialog, - setSelection, - url, - isValidURL, - urlLoading, - importFromURL, }; async function fetchImage() { @@ -238,51 +183,6 @@ export default defineComponent({ lightboxActive.value = false; editorActive.value = false; } - - function setSelection(selection: number[]) { - if (selection[0]) { - emit('input', selection[0]); - } else { - emit('input', null); - } - } - - function useURLImport() { - const url = ref(''); - const loading = ref(false); - const error = ref(null); - - const isValidURL = computed(() => { - try { - new URL(url.value); - return true; - } catch { - return false; - } - }); - - return { url, loading, error, isValidURL, importFromURL }; - - async function importFromURL() { - loading.value = true; - - try { - const response = await api.post(`/files/import`, { - url: url.value, - }); - - image.value = response.data.data; - - activeDialog.value = null; - url.value = ''; - emit('input', image.value?.id); - } catch (err) { - error.value = err; - } finally { - loading.value = false; - } - } - } }, }); @@ -402,17 +302,4 @@ img { .disabled-placeholder { height: var(--input-height-tall); } - -.options { - position: absolute; - top: 13px; - right: 13px; - color: var(--foreground-subdued); - cursor: pointer; - transition: color var(--medium) var(--transition); -} - -.image:hover .options { - color: var(--primary); -} diff --git a/app/src/modules/files/routes/add-new.vue b/app/src/modules/files/routes/add-new.vue index 863cbc11ed..72aefb2bf7 100644 --- a/app/src/modules/files/routes/add-new.vue +++ b/app/src/modules/files/routes/add-new.vue @@ -3,7 +3,7 @@ {{ $t('add_file') }} - + {{ $t('done') }} From b3639310c845e9173579070204c657d889150c79 Mon Sep 17 00:00:00 2001 From: rijkvanzanten Date: Thu, 24 Sep 2020 16:38:47 -0400 Subject: [PATCH 102/366] Fix wrong export --- app/src/composables/use-field-tree/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/composables/use-field-tree/index.ts b/app/src/composables/use-field-tree/index.ts index 7b57a783a2..1f1c1de159 100644 --- a/app/src/composables/use-field-tree/index.ts +++ b/app/src/composables/use-field-tree/index.ts @@ -1,4 +1,4 @@ -import useFieldTree, { filterTree, findTree } from './use-field-tree'; +import useFieldTree from './use-field-tree'; export default useFieldTree; -export { useFieldTree, filterTree, findTree }; +export { useFieldTree }; From 272653d5dde1231402b9dce6163677c745c12e23 Mon Sep 17 00:00:00 2001 From: rijkvanzanten Date: Thu, 24 Sep 2020 16:45:47 -0400 Subject: [PATCH 103/366] Use html attr style in components --- app/src/interfaces/file/file.vue | 2 +- app/src/interfaces/files/files.vue | 2 +- app/src/interfaces/image/image.vue | 2 +- app/src/modules/files/routes/add-new.vue | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/interfaces/file/file.vue b/app/src/interfaces/file/file.vue index 7d0f1ac39d..23e24ea2e8 100644 --- a/app/src/interfaces/file/file.vue +++ b/app/src/interfaces/file/file.vue @@ -71,7 +71,7 @@ {{ $t('upload_from_device') }} - + {{ $t('cancel') }} diff --git a/app/src/interfaces/files/files.vue b/app/src/interfaces/files/files.vue index 4126f8f936..0e27225792 100644 --- a/app/src/interfaces/files/files.vue +++ b/app/src/interfaces/files/files.vue @@ -59,7 +59,7 @@ {{ $t('upload_file') }} - + {{ $t('done') }} diff --git a/app/src/interfaces/image/image.vue b/app/src/interfaces/image/image.vue index 54134e385b..80867509dc 100644 --- a/app/src/interfaces/image/image.vue +++ b/app/src/interfaces/image/image.vue @@ -45,7 +45,7 @@ /> - + diff --git a/app/src/modules/files/routes/add-new.vue b/app/src/modules/files/routes/add-new.vue index 72aefb2bf7..b44d8911c4 100644 --- a/app/src/modules/files/routes/add-new.vue +++ b/app/src/modules/files/routes/add-new.vue @@ -3,7 +3,7 @@ {{ $t('add_file') }} - + {{ $t('done') }} From 07471b82081c48e4ab38d62a9a6bb2478a935a60 Mon Sep 17 00:00:00 2001 From: rijkvanzanten Date: Thu, 24 Sep 2020 17:15:37 -0400 Subject: [PATCH 104/366] Support primary keys in O2M patches Fixes #247 --- api/src/services/payload.ts | 19 ++++++++++++++----- .../routes/data-model/field-detail/store.ts | 6 ++++-- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/api/src/services/payload.ts b/api/src/services/payload.ts index e54c86623e..5ce68b2121 100644 --- a/api/src/services/payload.ts +++ b/api/src/services/payload.ts @@ -294,11 +294,20 @@ export class PayloadService { }); for (const relation of relationsToProcess) { - const relatedRecords: Partial[] = payload[relation.one_field].map( - (record: Partial) => ({ - ...record, - [relation.many_field]: parent || payload[relation.one_primary], - }) + const relatedRecords: Partial[] = payload[relation.one_field] + .map( + (record: string | number | Partial) => { + if (typeof record === 'string' || typeof record === 'number') { + record = { + [relation.many_primary]: record + }; + } + + return { + ...record, + [relation.many_field]: parent || payload[relation.one_primary], + } + } ); const itemsService = new ItemsService(relation.many_collection, { diff --git a/app/src/modules/settings/routes/data-model/field-detail/store.ts b/app/src/modules/settings/routes/data-model/field-detail/store.ts index 7122950771..034203f8e6 100644 --- a/app/src/modules/settings/routes/data-model/field-detail/store.ts +++ b/app/src/modules/settings/routes/data-model/field-detail/store.ts @@ -164,8 +164,9 @@ function initLocalStore( type: 'integer', schema: { has_auto_increment: true, + is_primary_key: true, }, - system: { + meta: { hidden: true, }, }, @@ -242,8 +243,9 @@ function initLocalStore( type: 'integer', schema: { has_auto_increment: true, + is_primary_key: true, }, - system: { + meta: { hidden: true, }, }, From 15add5b177e54f359498ecfb7ce373904249dffa Mon Sep 17 00:00:00 2001 From: rijkvanzanten Date: Thu, 24 Sep 2020 17:21:14 -0400 Subject: [PATCH 105/366] Save null for empty arrays in repeater Fixes #402 --- app/src/interfaces/repeater/repeater.vue | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/app/src/interfaces/repeater/repeater.vue b/app/src/interfaces/repeater/repeater.vue index 912ea0dd7a..78955192c0 100644 --- a/app/src/interfaces/repeater/repeater.vue +++ b/app/src/interfaces/repeater/repeater.vue @@ -69,8 +69,7 @@ export default defineComponent({ return { updateValues, onSort, removeItem, addNew, showAddNew, hideDragImage, selection }; function updateValues(index: number, updatedValues: any) { - emit( - 'input', + emitValue( props.value.map((item, i) => { if (i === index) { return updatedValues; @@ -82,18 +81,15 @@ export default defineComponent({ } function onSort(sortedItems: any[]) { - emit('input', sortedItems); + emitValue(sortedItems); } function removeItem(row: any) { selection.value = []; if (props.value) { - emit( - 'input', - props.value.filter((existingItem) => existingItem !== row) - ); + emitValue(props.value.filter((existingItem) => existingItem !== row)); } else { - emit('input', null); + emitValue(null); } } @@ -109,11 +105,19 @@ export default defineComponent({ selection.value = [props.value?.length || 0]; if (props.value !== null) { - emit('input', [...props.value, newDefaults]); + emitValue([...props.value, newDefaults]); } else { - emit('input', [newDefaults]); + emitValue([newDefaults]); } } + + function emitValue(value: null | any[]) { + if (value === null || value.length === 0) { + return emit('input', null); + } + + return emit('input', value); + } }, }); From 07f8064ea45ef696f8a67ef10929c230325dd851 Mon Sep 17 00:00:00 2001 From: rijkvanzanten Date: Thu, 24 Sep 2020 17:40:12 -0400 Subject: [PATCH 106/366] Fix o2m returning too many items --- api/src/database/run-ast.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/api/src/database/run-ast.ts b/api/src/database/run-ast.ts index fbc6aa136a..f91d0e904a 100644 --- a/api/src/database/run-ast.ts +++ b/api/src/database/run-ast.ts @@ -172,17 +172,22 @@ function mergeWithParentItems(nestedItem: Item | Item[], parentItem: Item | Item if (isM2O(nestedAST)) { for (const parentItem of parentItems) { - const itemChildren = nestedItems.filter((nestedItem) => { + const itemChild = nestedItems.find((nestedItem) => { return nestedItem[nestedAST.relation.one_primary] === parentItem[nestedAST.fieldKey]; }); - parentItem[nestedAST.fieldKey] = itemChildren; + parentItem[nestedAST.fieldKey] = itemChild || null; } } else { for (const parentItem of parentItems) { let itemChildren = nestedItems.filter((nestedItem) => { + if (nestedItem === null) return false; if (Array.isArray(nestedItem[nestedAST.relation.many_field])) return true; - return nestedItem[nestedAST.relation.many_field] === parentItem[nestedAST.relation.one_primary]; + + return ( + nestedItem[nestedAST.relation.many_field] === parentItem[nestedAST.relation.one_primary] || + nestedItem[nestedAST.relation.many_field]?.[nestedAST.relation.many_primary] === parentItem[nestedAST.relation.one_primary] + ); }); // We re-apply the requested limit here. This forces the _n_ nested items per parent concept @@ -210,10 +215,8 @@ function removeTemporaryFields(rawItem: Item | Item[], ast: AST | NestedCollecti const item = fields.includes('*') ? rawItem : pick(rawItem, fields); for (const nestedCollection of nestedCollections) { - item[nestedCollection.fieldKey] = removeTemporaryFields(Array.isArray(rawItem[nestedCollection.fieldKey]) ? rawItem[nestedCollection.fieldKey] : [rawItem[nestedCollection.fieldKey]], nestedCollection); - - if (isM2O(nestedCollection)) { - item[nestedCollection.fieldKey] = item[nestedCollection.fieldKey][0] || null; + if (item[nestedCollection.fieldKey] !== null) { + item[nestedCollection.fieldKey] = removeTemporaryFields(Array.isArray(rawItem[nestedCollection.fieldKey]) ? rawItem[nestedCollection.fieldKey] : [rawItem[nestedCollection.fieldKey]], nestedCollection); } } From 37013731c31a8354f216b7c19427b9c15364bbad Mon Sep 17 00:00:00 2001 From: Ben Haynes Date: Fri, 25 Sep 2020 09:35:54 -0400 Subject: [PATCH 107/366] Update contributing docs --- docs/getting-started/contributing.md | 70 ++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 10 deletions(-) diff --git a/docs/getting-started/contributing.md b/docs/getting-started/contributing.md index c9a5de9f60..7b893a5bc7 100644 --- a/docs/getting-started/contributing.md +++ b/docs/getting-started/contributing.md @@ -9,38 +9,88 @@ Our Node.js repository is located at `directus/directus` and houses the Admin Ap To contribute to the project, please follow the instructions located within our GitHub repoitory's [contributing.md file](#). :::tip PHP API Port -While our team's primary focus is the Node.js version, we do also support a community-lead PHP API port in Laravel. This secondary codebase is located in a separate git repository at `directus/php`. +While the Node.js version of Directus defines the official specification and is our team's primary focus, we also support a community-lead PHP API port in Laravel. This secondary codebase is located in a separate git repository at [`directus/php`](#). ::: ## Codebase Overview ### `/api` + +@TODO + ### `/app` + +@TODO + ### `/docs` ---- @TODO + ## Feature Requests -## The 80/20 Rule + +Feature requests are a great way to let our team know what should be prioritized next. You can [submit a feature request](https://github.com/directus/next/discussions/category_choices) or upvote [existing submissions](https://github.com/directus/next/discussions) all via our GitHub Discussions board. + +:::warning The 80/20 Rule To keep the Directus codebase as clean and simple as possible, we will only consider approving features that we feel at least 80% of our user-base will find valuable. If your feature request falls within the 20% range, it is considered an edge-case and should be implemented as an extension. +::: + ## RFCs + +Some Directus features/fixes may require additional design, strategy, and/or discussion before beginning work. For these notable pull-requests, you should first submit an RFC (Request For Comments) to our core team via [Discord](https://discord.gg/directus). This process is relatively informal, but ensures proper alignment, and helps avoid squandered development time by contributors. + ## Code of Conduct -## Documentation -Update any relevant docs with an PRs + +The Directus [Code of Conduct](https://github.com/directus/next/blob/main/code_of_conduct.md) is one of the ways we put our values into practice. We expect all of our staff, contractors and contributors to know and follow this code. + ## Bug Reporting + +@TODO + --- ## Prerequisites + +@TODO + ## Development Workflow -1) Clone the repo -2) Run `npx lerna bootstrap` +### 1. Fork the Directus repository -In case of unexpected weirdness, reinstall the dependencies by running +Go to the [repository](https://github.com/directus/next) and fork it to your GitHub account. +### 2. Clone from your repository + +```bash +git clone git@github.com:YOUR-USERNAME/next.git ``` + +### 3. Install the dependencies + +```bash +npx lerna bootstrap +``` + +### 4. Start the API development server + +```bash +cd directus/api +npm run dev +``` + +### 5. Start the App development server + +```bash +cd directus/app +npm run dev +``` + +:::tip Reinstalling Dependencies +```bash npx lerna clean -y && npx lerna bootstrap ``` - -You can run a dev server for the app and api by running `npm run dev` in the app or api folder respectively. +::: ## Before Submitting a Pull-Request + +### 1. Update Relevant Docs + +Before submitting any pull-requests, ensure that any relevant documentation (included in this same repo) is updated. From 1d168ff8789a2d54c413b6f0ac01474611ac9c25 Mon Sep 17 00:00:00 2001 From: rijkvanzanten Date: Fri, 25 Sep 2020 10:21:25 -0400 Subject: [PATCH 108/366] Fix m2o being returned as array --- api/src/database/run-ast.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/database/run-ast.ts b/api/src/database/run-ast.ts index f91d0e904a..a7c6403385 100644 --- a/api/src/database/run-ast.ts +++ b/api/src/database/run-ast.ts @@ -216,7 +216,7 @@ function removeTemporaryFields(rawItem: Item | Item[], ast: AST | NestedCollecti for (const nestedCollection of nestedCollections) { if (item[nestedCollection.fieldKey] !== null) { - item[nestedCollection.fieldKey] = removeTemporaryFields(Array.isArray(rawItem[nestedCollection.fieldKey]) ? rawItem[nestedCollection.fieldKey] : [rawItem[nestedCollection.fieldKey]], nestedCollection); + item[nestedCollection.fieldKey] = removeTemporaryFields(rawItem[nestedCollection.fieldKey], nestedCollection); } } From 450165986583ddd9856a2cba315707c8c180fb48 Mon Sep 17 00:00:00 2001 From: WoLfulus Date: Wed, 23 Sep 2020 04:03:13 -0300 Subject: [PATCH 109/366] add actions and workflows --- .actrc | 1 + .github/actions/build-images/.editorconfig | 13 + .github/actions/build-images/Dockerfile | 15 + .github/actions/build-images/action.yml | 31 ++ .../rootfs/directus/images/main/.dockerignore | 2 + .../rootfs/directus/images/main/.editorconfig | 13 + .../rootfs/directus/images/main/Dockerfile | 92 ++++++ .../rootfs/directus/images/main/package.json | 22 ++ .../images/main/rootfs/usr/bin/entrypoint | 110 +++++++ .../directus/images/main/rootfs/usr/bin/print | 48 +++ .../build-images/rootfs/usr/bin/entrypoint | 115 +++++++ .../build-images/rootfs/usr/bin/lib/argsf | 98 ++++++ .../build-images/rootfs/usr/bin/semver | 284 ++++++++++++++++++ .github/workflows/build-images.yml | 19 ++ Makefile | 19 ++ 15 files changed, 882 insertions(+) create mode 100644 .actrc create mode 100644 .github/actions/build-images/.editorconfig create mode 100644 .github/actions/build-images/Dockerfile create mode 100644 .github/actions/build-images/action.yml create mode 100644 .github/actions/build-images/rootfs/directus/images/main/.dockerignore create mode 100644 .github/actions/build-images/rootfs/directus/images/main/.editorconfig create mode 100644 .github/actions/build-images/rootfs/directus/images/main/Dockerfile create mode 100644 .github/actions/build-images/rootfs/directus/images/main/package.json create mode 100644 .github/actions/build-images/rootfs/directus/images/main/rootfs/usr/bin/entrypoint create mode 100644 .github/actions/build-images/rootfs/directus/images/main/rootfs/usr/bin/print create mode 100644 .github/actions/build-images/rootfs/usr/bin/entrypoint create mode 100644 .github/actions/build-images/rootfs/usr/bin/lib/argsf create mode 100644 .github/actions/build-images/rootfs/usr/bin/semver create mode 100644 .github/workflows/build-images.yml create mode 100644 Makefile diff --git a/.actrc b/.actrc new file mode 100644 index 0000000000..3fbeb49de3 --- /dev/null +++ b/.actrc @@ -0,0 +1 @@ +-P ubuntu-latest=nektos/act-environments-ubuntu:18.04 diff --git a/.github/actions/build-images/.editorconfig b/.github/actions/build-images/.editorconfig new file mode 100644 index 0000000000..071b6ae2a1 --- /dev/null +++ b/.github/actions/build-images/.editorconfig @@ -0,0 +1,13 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +tab_width = 2 +trim_trailing_whitespace = true + +[Makefile] +indent_style = tab diff --git a/.github/actions/build-images/Dockerfile b/.github/actions/build-images/Dockerfile new file mode 100644 index 0000000000..b9e58e9666 --- /dev/null +++ b/.github/actions/build-images/Dockerfile @@ -0,0 +1,15 @@ +FROM docker:stable + +RUN \ + apk update && \ + apk upgrade && \ + apk add bash + +COPY ./rootfs/ / + +RUN \ + chmod +x /usr/bin/lib/argsf && \ + chmod +x /usr/bin/entrypoint && \ + chmod +x /usr/bin/semver + +ENTRYPOINT ["entrypoint"] diff --git a/.github/actions/build-images/action.yml b/.github/actions/build-images/action.yml new file mode 100644 index 0000000000..1c42f496c3 --- /dev/null +++ b/.github/actions/build-images/action.yml @@ -0,0 +1,31 @@ +name: "Build and publish Directus images" +description: "GitHub Action to publish Directus container images." +branding: + icon: archive + color: gray-dark +inputs: + username: + description: "Repository user" + required: true + password: + description: "Repository password" + required: true + version: + description: "Version" + required: true + push: + description: "Push" + required: false + default: "false" +runs: + using: "docker" + image: "Dockerfile" + args: + - --username + - ${{ inputs.username }} + - --password + - ${{ inputs.password }} + - --version + - ${{ inputs.version }} + - --push + - ${{ inputs.push }} diff --git a/.github/actions/build-images/rootfs/directus/images/main/.dockerignore b/.github/actions/build-images/rootfs/directus/images/main/.dockerignore new file mode 100644 index 0000000000..6e19512a0e --- /dev/null +++ b/.github/actions/build-images/rootfs/directus/images/main/.dockerignore @@ -0,0 +1,2 @@ +.dockerignore +Dockerfile diff --git a/.github/actions/build-images/rootfs/directus/images/main/.editorconfig b/.github/actions/build-images/rootfs/directus/images/main/.editorconfig new file mode 100644 index 0000000000..071b6ae2a1 --- /dev/null +++ b/.github/actions/build-images/rootfs/directus/images/main/.editorconfig @@ -0,0 +1,13 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +tab_width = 2 +trim_trailing_whitespace = true + +[Makefile] +indent_style = tab diff --git a/.github/actions/build-images/rootfs/directus/images/main/Dockerfile b/.github/actions/build-images/rootfs/directus/images/main/Dockerfile new file mode 100644 index 0000000000..f113a4a015 --- /dev/null +++ b/.github/actions/build-images/rootfs/directus/images/main/Dockerfile @@ -0,0 +1,92 @@ +# +# Builder +# + +FROM node:14-alpine AS builder + +ARG VERSION + +RUN \ + apk update && \ + apk upgrade && \ + apk add jq + +WORKDIR /directus + +COPY package.json . +RUN \ + jq ".dependencies.directus = \"^${VERSION}\"" package.json > updated.json && \ + mv updated.json package.json + +RUN cat package.json + +# +# Image +# +FROM node:14-alpine + +ARG VERSION + +LABEL directus.version="${VERSION}" + +ENV \ + PORT=41201 \ + PUBLIC_URL="/" \ + DB_CLIENT="sqlite3" \ + DB_FILENAME="/directus/database/data.db" \ + RATE_LIMITER_ENABLED="false" \ + RATE_LIMITER_STORE="memory" \ + RATE_LIMITER_POINTS="25" \ + RATE_LIMITER_DURATION="1" \ + CACHE_ENABLED="false" \ + STORAGE_LOCATIONS="local" \ + STORAGE_LOCAL_PUBLIC_URL="/uploads" \ + STORAGE_LOCAL_DRIVER="local" \ + STORAGE_LOCAL_ROOT="/directus/uploads" \ + ACCESS_TOKEN_TTL="15m" \ + REFRESH_TOKEN_TTL="7d" \ + REFRESH_TOKEN_COOKIE_SECURE="false" \ + REFRESH_TOKEN_COOKIE_SAME_SITE="lax" \ + OAUTH_PROVIDERS="" \ + EXTENSIONS_PATH="/directus/extensions" \ + EMAIL_FROM="no-reply@directus.io" \ + EMAIL_TRANSPORT="sendmail" \ + EMAIL_SENDMAIL_NEW_LINE="unix" \ + EMAIL_SENDMAIL_PATH="/usr/sbin/sendmail" + +RUN \ + apk update && \ + apk upgrade && \ + apk add bash ssmtp util-linux + +SHELL ["/bin/bash", "-c"] + +WORKDIR /directus + +# Global requirements +RUN npm install -g yargs pino pino-colada + +# Install Directus +COPY --from=builder /directus/package.json . +RUN npm install + +# Copy files +COPY ./rootfs / +RUN chmod +x /usr/bin/entrypoint && chmod +x /usr/bin/print + +# Create directories +RUN \ + mkdir -p extensions/displays && \ + mkdir -p extensions/interfaces && \ + mkdir -p extensions/layouts && \ + mkdir -p extensions/modules && \ + mkdir -p database && \ + mkdir -p uploads + +EXPOSE 41201 +VOLUME \ + /directus/database \ + /directus/extensions \ + /directus/uploads + +ENTRYPOINT ["entrypoint"] diff --git a/.github/actions/build-images/rootfs/directus/images/main/package.json b/.github/actions/build-images/rootfs/directus/images/main/package.json new file mode 100644 index 0000000000..dddc406272 --- /dev/null +++ b/.github/actions/build-images/rootfs/directus/images/main/package.json @@ -0,0 +1,22 @@ +{ + "name": "directus-project", + "version": "1.0.0", + "description": "Directus Project", + "main": "index.js", + "scripts": {}, + "keywords": [], + "author": "", + "license": "MIT", + "dependencies": { + "@keyv/redis": "^2.1.2", + "directus": "^9.0.0-beta.1", + "ioredis": "^4.17.3", + "memcached": "^2.2.2", + "mssql": "^6.2.2", + "mysql": "^2.18.1", + "oracledb": "^5.0.0", + "pg": "^8.3.3", + "sqlite3": "^5.0.0", + "yargs": "^16.0.3" + } +} diff --git a/.github/actions/build-images/rootfs/directus/images/main/rootfs/usr/bin/entrypoint b/.github/actions/build-images/rootfs/directus/images/main/rootfs/usr/bin/entrypoint new file mode 100644 index 0000000000..a6ffc903cd --- /dev/null +++ b/.github/actions/build-images/rootfs/directus/images/main/rootfs/usr/bin/entrypoint @@ -0,0 +1,110 @@ +#!/usr/bin/env bash + +set -e + +function seed() { + # TODO: move users to a separate check, outside database installation + local show=false + local email=${DIRECTUS_ADMIN_EMAIL:-"admin@example.com"} + local password=${DIRECTUS_ADMIN_PASSWORD:-""} + + if [ "${password}" == "" ] ; then + password=$(node -e 'console.log(require("nanoid").nanoid(12))') + show=true + fi + + print --level=info "Creating administrator role" + local role=$(npx directus roles create --name Administrator --admin) + + print --level=info "Creating administrator user" + local user=$(npx directus users create --email "${email}" --password "${password}" --role "${role}") + + if [ "${show}" == "true" ] ; then + print --level=info --stdin < +> Email: $email +> Password: $password +> +MSG + else + print --level=info --stdin < +> Email: $email +> Password: +> +MSG + fi +} + +function bootstrap() { + local warn=false + + if [ "${KEY}" == "" ] ; then + export KEY=$(uuidgen) + warn=true + fi + + if [ "${SECRET}" == "" ] ; then + export SECRET=$(node -e 'console.log(require("nanoid").nanoid(32))') + warn=true + fi + + if [ "${warn}" == "true" ] ; then + print --level=warn --stdin < +> WARNING! +> +> The KEY and SECRET environment variables are not set. +> Some temporar +y variables were generated to fill the gap, +> but in production this is going to cause problems. +> +> Please refer to the docs at https://docs.directus.io/ +> on how and why to configure them properly +> +WARN + fi + + # Install database if using sqlite and file doesn't exist + if [ "${DB_CLIENT}" == "sqlite3" ] ; then + if [ "${DB_FILENAME}" == "" ] ; then + print --level=error "Missing DB_FILENAME environment variable" + exit 1 + fi + + if [ ! -f "${DB_FILENAME}" ] ; then + mkdir -p $(dirname ${DB_FILENAME}) + fi + fi + + should_seed=false + + set +e + npx directus database install 2>&1 /dev/null + if [ "$?" == "0" ] ; then + print --level=info "Database installed" + should_seed=true + fi + set -e + + if [ "${should_seed}" == "true" ] ; then + seed + fi +} + +command="" +if [ $# -eq 0 ] ; then + command="start" +elif [ "${1}" == "bash" ] || [ "${1}" == "shell" ] ; then + shift + exec bash $@ +elif [ "${1}" == "command" ] ; then + shift + exec $@ +else + command="${1}" + shift +fi + +bootstrap +exec npx directus "${command}" $@ diff --git a/.github/actions/build-images/rootfs/directus/images/main/rootfs/usr/bin/print b/.github/actions/build-images/rootfs/directus/images/main/rootfs/usr/bin/print new file mode 100644 index 0000000000..037ecd8f0f --- /dev/null +++ b/.github/actions/build-images/rootfs/directus/images/main/rootfs/usr/bin/print @@ -0,0 +1,48 @@ +#!/usr/bin/env node + +// Workarounds? +process.env.NODE_PATH = "/usr/local/lib/node_modules"; +require("module").Module._initPaths(); + +/** + * Read lines from stdin + */ +async function readlines() { + const chunks = []; + for await (const chunk of process.stdin) { + chunks.push(chunk); + } + + const lines = chunks.join("").split("\n"); + lines.pop(); + return lines; +} + +(async function () { + // Logger + const yargs = require("yargs"); + const logger = require("pino")({ + prettyPrint: process.env.LOG_STYLE !== "raw", + prettifier: require("pino-colada"), + level: process.env.LOG_LEVEL || "info", + }); + + function write(...message) { + if (level in logger) { + logger[level](...message); + } else { + logger.info(...message); + } + } + + const args = yargs.argv; + const level = args.level || "info"; + const stdin = args.stdin || false; + + if (stdin) { + const lines = await readlines(); + lines.forEach((line) => write(line)); + } else { + write(...args._); + } +})(); diff --git a/.github/actions/build-images/rootfs/usr/bin/entrypoint b/.github/actions/build-images/rootfs/usr/bin/entrypoint new file mode 100644 index 0000000000..1c411b1263 --- /dev/null +++ b/.github/actions/build-images/rootfs/usr/bin/entrypoint @@ -0,0 +1,115 @@ +#!/usr/bin/env bash + +set -e + +root=$(dirname ${0}) +source ${root}/lib/argsf + +# +# Makes a set of tags +# +function make_tags() { + local prefix="" + local version=${1} + + semver get major ${version} > /dev/null 2>&1 + if [ "$?" != "0" ]; then + echo "${version}" + else + if [ "${version:0:1}" == "v" ]; then + prefix="v" + fi + + major="$(semver get major ${version})" + minor="${major}.$(semver get minor ${version})" + patch="${minor}.$(semver get patch ${version})" + + prerel="$(semver get prerel ${version})" + if [ "${prerel}" == "" ]; then + is_prerel=false + else + is_prerel=true + fi + + build="$(semver get build ${version})" + if [ "${build}" == "" ]; then + is_build=false + else + is_build=true + fi + + if [ "${is_prerel}" == "true" ]; then + echo "${prefix}${major}-${prerel}" + echo "${prefix}${minor}-${prerel}" + echo "${prefix}${patch}-${prerel}" + if [ "${is_build}" == "true" ]; then + echo "${prefix}${major}-${prerel}-${build}" + fi + else + echo "${prefix}${major}" + echo "${prefix}${minor}" + echo "${prefix}${patch}" + if [ "${is_build}" == "true" ]; then + echo "${prefix}${patch}-${build}" + fi + fi + fi +} + +# +# Build script +# +function main() { + username=$(argument username) + password=$(argument password) + + push=$(argument push "false") + latest=$(argument latest "false") + + repository=$(argument repository "ghcr.io") + image=$(argument image "directus/directus") + version=$(argument version "") + context=$(argument context ".") + + # Normalize tag + if [ "${version}" == "" ]; then + version=${GITHUB_REF##*/} + else + version=${version##*/} + fi + + if [ "${version}" == "" ]; then + version=$(echo ${GITHUB_SHA:-"000000000000"} | cut -c1-12) + fi + + tags=$(make_tags ${version}) + + # login into registry + docker login -u "${username}" -p "${password}" "${repository}" + docker build \ + -t directus:main \ + --build-arg VERSION=${version} \ + /directus/images/main + + if [ "${latest}" == "true" ]; then + docker tag directus:main "${repository}/${image}:latest" + if [ "${push}" == "true" ]; then + #docker push ${image} + echo "should push" + fi + fi + + for tag in $tags + do + docker tag directus:main "${repository}/${image}:${tag}" + if [ "${push}" == "true" ]; then + #docker push ${image} + echo "should push" + fi + done + + exit $? +} + +main +exit $? diff --git a/.github/actions/build-images/rootfs/usr/bin/lib/argsf b/.github/actions/build-images/rootfs/usr/bin/lib/argsf new file mode 100644 index 0000000000..0869fa25bd --- /dev/null +++ b/.github/actions/build-images/rootfs/usr/bin/lib/argsf @@ -0,0 +1,98 @@ +# +# Arguments and Flags (argsf) +# This is meant to work with bash shell +# To use, source this file into your bash scripts +# +# Implemented by João Biondo +# https://github.com/WoLfulus/argsf +# + +declare _ARGCOUNT=$# +declare _ARGDATA=("$@") +declare -A _ARGMAP +declare -A _FLAGMAP + +for ((_arg_index_key=1;_arg_index_key<=$#;_arg_index_key++)) +do + _arg_index_value=$(expr $_arg_index_key + 1) + _arg_key=${!_arg_index_key} + _arg_value=${!_arg_index_value} + if [[ $_arg_key == *"--"* ]]; then + if [[ $_arg_key == *" "* ]]; then + continue + fi + _arg_name="${_arg_key:2}" + _FLAGMAP[${_arg_name}]=1 + if [[ $_arg_value != *"--"* ]] || [[ $_arg_value == *" "* ]] ; then + _ARGMAP[${_arg_name}]="$_arg_value" + else + _ARGMAP[${_arg_name}]="" + fi + fi +done + +function _argument() { + if test "${_ARGMAP[${ARG_NAME}]+isset}" ; then + echo ${_ARGMAP[${ARG_NAME}]} + else + if [ ${ARG_DEFAULT} -eq 0 ]; then + echo "Error: required argument '--${ARG_NAME}' not specified" 1>&2 + exit 1 + else + echo ${ARG_DEFAULT_VALUE} + fi + fi +} + +function argument() { + if [ $# -eq 1 ]; then + ARG_NAME="$1" ARG_DEFAULT=0 ARG_DEFAULT_VALUE= _argument "${_ARGUMENT_DATA}" + elif [ $# -eq 2 ]; then + ARG_NAME="$1" ARG_DEFAULT=1 ARG_DEFAULT_VALUE="$2" _argument "${_ARGUMENT_DATA}" + else + echo "argument: invalid number of arguments" 1>&2 + return 1 + fi + return 0 +} + +function flage() { + if [ $# -eq 1 ]; then + if [[ ${_FLAGMAP[$1]} ]] ; then + echo "true" + return 0 + elif [[ ${_FLAGMAP[no-$1]} ]] ; then + echo "false" + return 0 + else + echo "true" + return 0 + fi + else + echo "flag: invalid number of arguments" 1>&2 + return 1 + fi +} + +function flagd() { + if [ $# -eq 1 ]; then + if [[ ${_FLAGMAP[$1]} ]] ; then + echo "true" + return 0 + elif [[ ${_FLAGMAP[no-$1]} ]] ; then + echo "false" + return 0 + else + echo "false" + return 0 + fi + else + echo "flag: invalid number of arguments" 1>&2 + return 1 + fi +} + +function flag() { + flagd $1 + return $? +} diff --git a/.github/actions/build-images/rootfs/usr/bin/semver b/.github/actions/build-images/rootfs/usr/bin/semver new file mode 100644 index 0000000000..c3d5075162 --- /dev/null +++ b/.github/actions/build-images/rootfs/usr/bin/semver @@ -0,0 +1,284 @@ +#!/usr/bin/env bash + +# +# Copyright (c) 2014-2015 François Saint-Jacques +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# this program. If not, see . +# + +set -o errexit -o nounset -o pipefail + +NAT='0|[1-9][0-9]*' +ALPHANUM='[0-9]*[A-Za-z-][0-9A-Za-z-]*' +IDENT="$NAT|$ALPHANUM" +FIELD='[0-9A-Za-z-]+' + +SEMVER_REGEX="\ +^[vV]?\ +($NAT)\\.($NAT)\\.($NAT)\ +(\\-(${IDENT})(\\.(${IDENT}))*)?\ +(\\+${FIELD}(\\.${FIELD})*)?$" + +PROG=semver +PROG_VERSION="3.0.0" + +USAGE="\ +Usage: + $PROG bump (major|minor|patch|release|prerel |build ) + $PROG compare + $PROG get (major|minor|patch|release|prerel|build) + $PROG --help + $PROG --version +Arguments: + A version must match the following regular expression: + \"${SEMVER_REGEX}\" + In English: + -- The version must match X.Y.Z[-PRERELEASE][+BUILD] + where X, Y and Z are non-negative integers. + -- PRERELEASE is a dot separated sequence of non-negative integers and/or + identifiers composed of alphanumeric characters and hyphens (with + at least one non-digit). Numeric identifiers must not have leading + zeros. A hyphen (\"-\") introduces this optional part. + -- BUILD is a dot separated sequence of identifiers composed of alphanumeric + characters and hyphens. A plus (\"+\") introduces this optional part. + See definition. + A string as defined by PRERELEASE above. + A string as defined by BUILD above. +Options: + -v, --version Print the version of this tool. + -h, --help Print this help message. +Commands: + bump Bump by one of major, minor, patch; zeroing or removing + subsequent parts. \"bump prerel\" sets the PRERELEASE part and + removes any BUILD part. \"bump build\" sets the BUILD part. + \"bump release\" removes any PRERELEASE or BUILD parts. + The bumped version is written to stdout. + compare Compare with , output to stdout the + following values: -1 if is newer, 0 if equal, 1 if + older. The BUILD part is not used in comparisons. + get Extract given part of , where part is one of major, minor, + patch, prerel, build, or release. +See also: + https://semver.org -- Semantic Versioning 2.0.0" + +function error { + echo -e "$1" >&2 + exit 1 +} + +function usage-help { + error "$USAGE" +} + +function usage-version { + echo -e "${PROG}: $PROG_VERSION" + exit 0 +} + +function validate-version { + local version=$1 + if [[ "$version" =~ $SEMVER_REGEX ]]; then + # if a second argument is passed, store the result in var named by $2 + if [ "$#" -eq "2" ]; then + local major=${BASH_REMATCH[1]} + local minor=${BASH_REMATCH[2]} + local patch=${BASH_REMATCH[3]} + local prere=${BASH_REMATCH[4]} + local build=${BASH_REMATCH[8]} + eval "$2=(\"$major\" \"$minor\" \"$patch\" \"$prere\" \"$build\")" + else + echo "$version" + fi + else + error "version $version does not match the semver scheme 'X.Y.Z(-PRERELEASE)(+BUILD)'. See help for more information." + fi +} + +function is-nat { + [[ "$1" =~ ^($NAT)$ ]] +} + +function is-null { + [ -z "$1" ] +} + +function order-nat { + [ "$1" -lt "$2" ] && { echo -1 ; return ; } + [ "$1" -gt "$2" ] && { echo 1 ; return ; } + echo 0 +} + +function order-string { + [[ $1 < $2 ]] && { echo -1 ; return ; } + [[ $1 > $2 ]] && { echo 1 ; return ; } + echo 0 +} + +# given two (named) arrays containing NAT and/or ALPHANUM fields, compare them +# one by one according to semver 2.0.0 spec. Return -1, 0, 1 if left array ($1) +# is less-than, equal, or greater-than the right array ($2). The longer array +# is considered greater-than the shorter if the shorter is a prefix of the longer. +# +function compare-fields { + local l="$1[@]" + local r="$2[@]" + local leftfield=( "${!l}" ) + local rightfield=( "${!r}" ) + local left + local right + + local i=$(( -1 )) + local order=$(( 0 )) + + while true + do + [ $order -ne 0 ] && { echo $order ; return ; } + + : $(( i++ )) + left="${leftfield[$i]}" + right="${rightfield[$i]}" + + is-null "$left" && is-null "$right" && { echo 0 ; return ; } + is-null "$left" && { echo -1 ; return ; } + is-null "$right" && { echo 1 ; return ; } + + is-nat "$left" && is-nat "$right" && { order=$(order-nat "$left" "$right") ; continue ; } + is-nat "$left" && { echo -1 ; return ; } + is-nat "$right" && { echo 1 ; return ; } + { order=$(order-string "$left" "$right") ; continue ; } + done +} + +# shellcheck disable=SC2206 # checked by "validate"; ok to expand prerel id's into array +function compare-version { + local order + validate-version "$1" V + validate-version "$2" V_ + + # compare major, minor, patch + + local left=( "${V[0]}" "${V[1]}" "${V[2]}" ) + local right=( "${V_[0]}" "${V_[1]}" "${V_[2]}" ) + + order=$(compare-fields left right) + [ "$order" -ne 0 ] && { echo "$order" ; return ; } + + # compare pre-release ids when M.m.p are equal + + local prerel="${V[3]:1}" + local prerel_="${V_[3]:1}" + local left=( ${prerel//./ } ) + local right=( ${prerel_//./ } ) + + # if left and right have no pre-release part, then left equals right + # if only one of left/right has pre-release part, that one is less than simple M.m.p + + [ -z "$prerel" ] && [ -z "$prerel_" ] && { echo 0 ; return ; } + [ -z "$prerel" ] && { echo 1 ; return ; } + [ -z "$prerel_" ] && { echo -1 ; return ; } + + # otherwise, compare the pre-release id's + + compare-fields left right +} + +function command-bump { + local new; local version; local sub_version; local command; + + case $# in + 2) case $1 in + major|minor|patch|release) command=$1; version=$2;; + *) usage-help;; + esac ;; + 3) case $1 in + prerel|build) command=$1; sub_version=$2 version=$3 ;; + *) usage-help;; + esac ;; + *) usage-help;; + esac + + validate-version "$version" parts + # shellcheck disable=SC2154 + local major="${parts[0]}" + local minor="${parts[1]}" + local patch="${parts[2]}" + local prere="${parts[3]}" + local build="${parts[4]}" + + case "$command" in + major) new="$((major + 1)).0.0";; + minor) new="${major}.$((minor + 1)).0";; + patch) new="${major}.${minor}.$((patch + 1))";; + release) new="${major}.${minor}.${patch}";; + prerel) new=$(validate-version "${major}.${minor}.${patch}-${sub_version}");; + build) new=$(validate-version "${major}.${minor}.${patch}${prere}+${sub_version}");; + *) usage-help ;; + esac + + echo "$new" + exit 0 +} + +function command-compare { + local v; local v_; + + case $# in + 2) v=$(validate-version "$1"); v_=$(validate-version "$2") ;; + *) usage-help ;; + esac + + set +u # need unset array element to evaluate to null + compare-version "$v" "$v_" + exit 0 +} + + +# shellcheck disable=SC2034 +function command-get { + local part version + + if [[ "$#" -ne "2" ]] || [[ -z "$1" ]] || [[ -z "$2" ]]; then + usage-help + exit 0 + fi + + part="$1" + version="$2" + + validate-version "$version" parts + local major="${parts[0]}" + local minor="${parts[1]}" + local patch="${parts[2]}" + local prerel="${parts[3]:1}" + local build="${parts[4]:1}" + local release="${major}.${minor}.${patch}" + + case "$part" in + major|minor|patch|release|prerel|build) echo "${!part}" ;; + *) usage-help ;; + esac + + exit 0 +} + +case $# in + 0) echo "Unknown command: $*"; usage-help;; +esac + +case $1 in + --help|-h) echo -e "$USAGE"; exit 0;; + --version|-v) usage-version ;; + bump) shift; command-bump "$@";; + get) shift; command-get "$@";; + compare) shift; command-compare "$@";; + *) echo "Unknown arguments: $*"; usage-help;; +esac diff --git a/.github/workflows/build-images.yml b/.github/workflows/build-images.yml new file mode 100644 index 0000000000..56b37c4a79 --- /dev/null +++ b/.github/workflows/build-images.yml @@ -0,0 +1,19 @@ +name: build-images +on: + release: + types: + - created + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Build + uses: ./.github/actions/build-images + with: + username: ${{ github.actor }} + password: ${{ github.token }} + version: ${{ github.ref }} diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000..8f6d9ff5c5 --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +SHELL=bash + +version=v9.0.0-beta.1 +tag=$(version) +cmd= + +.PHONY: build + +action-test: + act release -a directus + +build-image: + docker build --build-arg VERSION=$(version) -t directus:temp -f ./containers/Dockerfile ./containers + docker tag directus:temp ghcr.io/directus/directus:$(version) + docker tag directus:temp ghcr.io/directus/directus:$(tag) + docker image rm directus:temp + +run-image: + docker run --rm -it ghcr.io/directus/directus:$(tag) $(cmd) From 4539ab22100adbbddeb7107412afedb6ee6ba185 Mon Sep 17 00:00:00 2001 From: WoLfulus Date: Wed, 23 Sep 2020 04:03:34 -0300 Subject: [PATCH 110/366] add process signal processing --- .editorconfig | 8 ++++++++ api/src/cli/commands/start.ts | 18 ++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/.editorconfig b/.editorconfig index d2cb980c62..52129378e7 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,3 +11,11 @@ trim_trailing_whitespace = true [{package.json,*.yml,*.yaml}] indent_style = space indent_size = 2 + +[Dockerfile] +indent_size = 2 +indent_style = tab + +[Makefile] +indent_size = 2 +indent_style = tab diff --git a/api/src/cli/commands/start.ts b/api/src/cli/commands/start.ts index f41bba2edc..f6fef91a43 100644 --- a/api/src/cli/commands/start.ts +++ b/api/src/cli/commands/start.ts @@ -1,4 +1,5 @@ import logger from '../../logger'; +import { Express } from 'express'; export default async function start() { const { default: env } = require('../../env'); @@ -6,11 +7,24 @@ export default async function start() { await validateDBConnection(); - const app = require('../../app').default; + const app: Express = require('../../app').default; const port = env.PORT; - app.listen(port, () => { + const server = app.listen(port, () => { logger.info(`Server started at port ${port}`); }); + + const signals: NodeJS.Signals[] = ['SIGHUP', 'SIGINT', 'SIGTERM']; + signals.forEach((signal) => { + process.on(signal, () => + server.close((err) => { + if (err) { + logger.error(err.message, { err }); + return; + } + logger.info('Server stopped.'); + }) + ); + }); } From 5617c29d6307b9a597d099ee0e84015ead90b8f7 Mon Sep 17 00:00:00 2001 From: WoLfulus Date: Wed, 23 Sep 2020 04:24:59 -0300 Subject: [PATCH 111/366] small fixes --- .github/actions/build-images/rootfs/usr/bin/entrypoint | 1 + .gitignore | 1 + Makefile | 7 +++++-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/actions/build-images/rootfs/usr/bin/entrypoint b/.github/actions/build-images/rootfs/usr/bin/entrypoint index 1c411b1263..abe52a7713 100644 --- a/.github/actions/build-images/rootfs/usr/bin/entrypoint +++ b/.github/actions/build-images/rootfs/usr/bin/entrypoint @@ -83,6 +83,7 @@ function main() { fi tags=$(make_tags ${version}) + echo "Tags = ${tags}" # login into registry docker login -u "${username}" -p "${password}" "${repository}" diff --git a/.gitignore b/.gitignore index f0441eb2ab..222195d603 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ node_modules .vs_code .env +.secrets npm-debug.log lerna-debug.log .nova diff --git a/Makefile b/Makefile index 8f6d9ff5c5..58ba32268d 100644 --- a/Makefile +++ b/Makefile @@ -3,11 +3,14 @@ SHELL=bash version=v9.0.0-beta.1 tag=$(version) cmd= +user=directus .PHONY: build -action-test: - act release -a directus +action: + act release \ + --actor $(user) \ + --secret-file .secrets build-image: docker build --build-arg VERSION=$(version) -t directus:temp -f ./containers/Dockerfile ./containers From a151a1a51533b3b8a546bb8868768097abd058f8 Mon Sep 17 00:00:00 2001 From: WoLfulus Date: Wed, 23 Sep 2020 16:29:43 -0300 Subject: [PATCH 112/366] several pipeline fixes --- .github/actions/build-images/action.yml | 15 +++++++- .../rootfs/directus/images/main/Dockerfile | 6 ++- .../build-images/rootfs/usr/bin/entrypoint | 37 ++++++++++++++----- .github/workflows/build-images.yml | 11 ++++-- Makefile | 16 ++++++-- 5 files changed, 63 insertions(+), 22 deletions(-) diff --git a/.github/actions/build-images/action.yml b/.github/actions/build-images/action.yml index 1c42f496c3..40dfba9789 100644 --- a/.github/actions/build-images/action.yml +++ b/.github/actions/build-images/action.yml @@ -4,11 +4,18 @@ branding: icon: archive color: gray-dark inputs: + repository: + description: "Repository name" + required: true + registry: + description: "Registry" + required: false + default: ghcr.io username: - description: "Repository user" + description: "Registry user" required: true password: - description: "Repository password" + description: "Registry password" required: true version: description: "Version" @@ -21,6 +28,10 @@ runs: using: "docker" image: "Dockerfile" args: + - --registry + - ${{ inputs.registry }} + - --repository + - ${{ inputs.repository }} - --username - ${{ inputs.username }} - --password diff --git a/.github/actions/build-images/rootfs/directus/images/main/Dockerfile b/.github/actions/build-images/rootfs/directus/images/main/Dockerfile index f113a4a015..7f22d6fa0e 100644 --- a/.github/actions/build-images/rootfs/directus/images/main/Dockerfile +++ b/.github/actions/build-images/rootfs/directus/images/main/Dockerfile @@ -26,14 +26,16 @@ RUN cat package.json FROM node:14-alpine ARG VERSION +ARG REPOSITORY=directus/directus LABEL directus.version="${VERSION}" +LABEL org.opencontainers.image.source https://github.com/${REPOSITORY} ENV \ - PORT=41201 \ + PORT="41201" \ PUBLIC_URL="/" \ DB_CLIENT="sqlite3" \ - DB_FILENAME="/directus/database/data.db" \ + DB_FILENAME="/directus/database/database.sqlite" \ RATE_LIMITER_ENABLED="false" \ RATE_LIMITER_STORE="memory" \ RATE_LIMITER_POINTS="25" \ diff --git a/.github/actions/build-images/rootfs/usr/bin/entrypoint b/.github/actions/build-images/rootfs/usr/bin/entrypoint index abe52a7713..16ebb0020c 100644 --- a/.github/actions/build-images/rootfs/usr/bin/entrypoint +++ b/.github/actions/build-images/rootfs/usr/bin/entrypoint @@ -66,8 +66,12 @@ function main() { push=$(argument push "false") latest=$(argument latest "false") - repository=$(argument repository "ghcr.io") - image=$(argument image "directus/directus") + registry=$(argument registry "ghcr.io") + registry=$(echo "${registry}" | tr '[:upper:]' '[:lower:]') + + repository=$(argument repository "directus/next") + repository=$(echo "${repository}" | tr '[:upper:]' '[:lower:]') + version=$(argument version "") context=$(argument context ".") @@ -85,30 +89,43 @@ function main() { tags=$(make_tags ${version}) echo "Tags = ${tags}" - # login into registry - docker login -u "${username}" -p "${password}" "${repository}" + # build image docker build \ -t directus:main \ --build-arg VERSION=${version} \ + --build-arg REPOSITORY=${repository} \ /directus/images/main + # login into registry + docker login -u "${username}" -p "${password}" "${registry}" + + # Push latest + # TODO: check if it's really the latest if [ "${latest}" == "true" ]; then - docker tag directus:main "${repository}/${image}:latest" + fqin="${registry}/${repository}:latest" + echo "Tagging ${fqin}" + docker tag directus:main ${fqin} if [ "${push}" == "true" ]; then - #docker push ${image} - echo "should push" + echo "Pushing tag ${fqin}" + docker push "${fqin}" fi fi + # Push tags for tag in $tags do - docker tag directus:main "${repository}/${image}:${tag}" + tag=$(echo "${tag}" | tr '[:upper:]' '[:lower:]') + fqin="${registry}/${repository}:latest" + echo "Tagging ${fqin}" + docker tag directus:main "${registry}/${repository}:${tag}" if [ "${push}" == "true" ]; then - #docker push ${image} - echo "should push" + echo "Pushing tag ${fqin}" + docker push "${registry}/${repository}:${tag}" fi done + echo "Finished." + exit $? } diff --git a/.github/workflows/build-images.yml b/.github/workflows/build-images.yml index 56b37c4a79..2e70b28ff5 100644 --- a/.github/workflows/build-images.yml +++ b/.github/workflows/build-images.yml @@ -2,7 +2,7 @@ name: build-images on: release: types: - - created + - published jobs: build: @@ -14,6 +14,9 @@ jobs: - name: Build uses: ./.github/actions/build-images with: - username: ${{ github.actor }} - password: ${{ github.token }} - version: ${{ github.ref }} + registry: "ghcr.io" + repository: "${{ github.repository }}" + username: "${{ secrets.REGISTRY_USERNAME }}" + password: "${{ secrets.REGISTRY_PASSWORD }}" + version: "${{ github.ref }}" + push: "true" diff --git a/Makefile b/Makefile index 58ba32268d..e2ede49215 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,8 @@ version=v9.0.0-beta.1 tag=$(version) cmd= user=directus +registry=ghcr.io +repository=directus/next .PHONY: build @@ -13,10 +15,16 @@ action: --secret-file .secrets build-image: - docker build --build-arg VERSION=$(version) -t directus:temp -f ./containers/Dockerfile ./containers - docker tag directus:temp ghcr.io/directus/directus:$(version) - docker tag directus:temp ghcr.io/directus/directus:$(tag) + docker build \ + --build-arg VERSION=$(version) \ + --build-arg REPOSITORY=$(repository) \ + -t directus:temp \ + -f ./.github/actions/build-images/rootfs/directus/images/main/Dockerfile \ + ./.github/actions/build-images/rootfs/directus/images/main + + docker tag directus:temp $(registry)/$(repository):$(version) + docker tag directus:temp $(registry)/$(repository):$(tag) docker image rm directus:temp run-image: - docker run --rm -it ghcr.io/directus/directus:$(tag) $(cmd) + docker run --rm -it $(registry)/$(repository):$(tag) $(cmd) From 250c2f46a734c313cecff97dfe66343638667f69 Mon Sep 17 00:00:00 2001 From: WoLfulus Date: Wed, 23 Sep 2020 17:01:35 -0300 Subject: [PATCH 113/366] remote actrc --- .actrc | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .actrc diff --git a/.actrc b/.actrc deleted file mode 100644 index 3fbeb49de3..0000000000 --- a/.actrc +++ /dev/null @@ -1 +0,0 @@ --P ubuntu-latest=nektos/act-environments-ubuntu:18.04 From 4cc11371f1c85a2e7476e22e83ead68cee1f0c31 Mon Sep 17 00:00:00 2001 From: WoLfulus Date: Fri, 25 Sep 2020 11:36:39 -0300 Subject: [PATCH 114/366] move makefile --- Makefile => .github/actions/Makefile | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) rename Makefile => .github/actions/Makefile (66%) diff --git a/Makefile b/.github/actions/Makefile similarity index 66% rename from Makefile rename to .github/actions/Makefile index e2ede49215..73f4947538 100644 --- a/Makefile +++ b/.github/actions/Makefile @@ -9,22 +9,17 @@ repository=directus/next .PHONY: build -action: - act release \ - --actor $(user) \ - --secret-file .secrets - -build-image: +build-images: docker build \ --build-arg VERSION=$(version) \ --build-arg REPOSITORY=$(repository) \ -t directus:temp \ - -f ./.github/actions/build-images/rootfs/directus/images/main/Dockerfile \ - ./.github/actions/build-images/rootfs/directus/images/main + -f ./build-images/rootfs/directus/images/main/Dockerfile \ + ./build-images/rootfs/directus/images/main docker tag directus:temp $(registry)/$(repository):$(version) docker tag directus:temp $(registry)/$(repository):$(tag) docker image rm directus:temp -run-image: +test-image: docker run --rm -it $(registry)/$(repository):$(tag) $(cmd) From f5d4e670fe99f9bb496da6c3254096a04672aff5 Mon Sep 17 00:00:00 2001 From: Ben Haynes Date: Fri, 25 Sep 2020 10:57:48 -0400 Subject: [PATCH 115/366] Fixes #330 --- app/src/components/v-pagination/v-pagination.vue | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/components/v-pagination/v-pagination.vue b/app/src/components/v-pagination/v-pagination.vue index a471a9a8e8..452431a888 100644 --- a/app/src/components/v-pagination/v-pagination.vue +++ b/app/src/components/v-pagination/v-pagination.vue @@ -1,11 +1,11 @@