Browse Source

Merge branch 'develop' into fix/formula-empty-result

pull/4644/head
Wing-Kam Wong 2 years ago
parent
commit
8d27065f47
  1. 32
      .github/uffizzi/docker-compose.uffizzi.yml
  2. 8
      .github/workflows/release-docker.yml
  3. 5
      packages/nc-gui/components/general/ReleaseInfo.vue
  4. 100
      packages/nc-gui/lang/ru.json
  5. 3
      packages/nc-gui/pages/[projectType]/[projectId]/index/index/index.vue
  6. 18
      packages/noco-docs/content/en/engineering/builds-and-releases.md
  7. 11
      packages/nocodb/package-lock.json
  8. 1
      packages/nocodb/package.json
  9. 45
      packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/formulav2/formulaQueryBuilderv2.ts
  10. 29
      packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/helpers/formulaFnHelper.ts
  11. 22
      packages/nocodb/src/lib/meta/api/utilApis.ts

32
.github/uffizzi/docker-compose.uffizzi.yml

@ -8,17 +8,45 @@ x-uffizzi:
services:
postgres:
image: postgres
restart: always
environment:
POSTGRES_PASSWORD: password
POSTGRES_USER: postgres
POSTGRES_DB: root_db
deploy:
resources:
limits:
memory: 500M
mssql:
image: "mcr.microsoft.com/mssql/server:2017-latest"
environment:
ACCEPT_EULA: "Y"
SA_PASSWORD: Password123.
deploy:
resources:
limits:
memory: 1000M
mysql:
environment:
MYSQL_DATABASE: root_db
MYSQL_PASSWORD: password
MYSQL_ROOT_PASSWORD: password
MYSQL_USER: noco
image: "mysql:5.7"
deploy:
resources:
limits:
memory: 500M
nocodb:
image: "${NOCODB_IMAGE}"
ports:
- "8080:8080"
restart: always
entrypoint: /bin/sh
command: ["-c", "apk add wait4ports && wait4ports tcp://localhost:5432 && /usr/src/appEntry/start.sh"]
environment:
NC_DB: "pg://localhost:5432?u=postgres&p=password&d=root_db"
NC_ADMIN_EMAIL: admin@nocodb.com
NC_ADMIN_PASSWORD: password
deploy:
resources:
limits:
memory: 500M

8
.github/workflows/release-docker.yml

@ -50,6 +50,10 @@ jobs:
run: |
DOCKER_REPOSITORY=nocodb
DOCKER_BUILD_TAG=${{ github.event.inputs.tag || inputs.tag }}
DOCKER_BUILD_LATEST_TAG=latest
if [[ "$DOCKER_BUILD_TAG" =~ "-beta." ]]; then
DOCKER_BUILD_LATEST_TAG=$(echo $DOCKER_BUILD_TAG | awk -F '-beta.' '{print $1}')-beta.latest
fi
if [[ ${{ github.event.inputs.targetEnv || inputs.targetEnv }} == 'DEV' ]]; then
if [[ ${{ github.event.inputs.currentVersion || inputs.currentVersion || 'N/A' }} != 'N/A' ]]; then
DOCKER_BUILD_TAG=${{ github.event.inputs.currentVersion || inputs.currentVersion }}-${{ github.event.inputs.tag || inputs.tag }}
@ -62,8 +66,10 @@ jobs:
fi
echo "DOCKER_REPOSITORY=${DOCKER_REPOSITORY}" >> $GITHUB_OUTPUT
echo "DOCKER_BUILD_TAG=${DOCKER_BUILD_TAG}" >> $GITHUB_OUTPUT
echo "DOCKER_BUILD_LATEST_TAG=${DOCKER_BUILD_LATEST_TAG}" >> $GITHUB_OUTPUT
echo DOCKER_REPOSITORY: ${DOCKER_REPOSITORY}
echo DOCKER_BUILD_TAG: ${DOCKER_BUILD_TAG}
echo DOCKER_BUILD_LATEST_TAG: ${DOCKER_BUILD_LATEST_TAG}
- name: Checkout
uses: actions/checkout@v3
@ -134,7 +140,7 @@ jobs:
push: true
tags: |
nocodb/${{ steps.get-docker-repository.outputs.DOCKER_REPOSITORY }}:${{ steps.get-docker-repository.outputs.DOCKER_BUILD_TAG }}
nocodb/${{ steps.get-docker-repository.outputs.DOCKER_REPOSITORY }}:latest
nocodb/${{ steps.get-docker-repository.outputs.DOCKER_REPOSITORY }}:${{ steps.get-docker-repository.outputs.DOCKER_BUILD_LATEST_TAG }}
# Temp fix
# https://github.com/docker/build-push-action/issues/252

5
packages/nc-gui/components/general/ReleaseInfo.vue

@ -7,6 +7,9 @@ const { currentVersion, latestRelease, hiddenRelease } = useGlobal()
const releaseAlert = computed({
get() {
if (currentVersion.value?.includes('-beta.') || latestRelease.value?.includes('-beta.')) {
return false
}
return (
currentVersion.value &&
latestRelease.value &&
@ -22,7 +25,7 @@ const releaseAlert = computed({
async function fetchReleaseInfo() {
try {
const versionInfo = await $api.utils.appVersion()
if (versionInfo && versionInfo.releaseVersion && versionInfo.currentVersion && !/[^0-9.]/.test(versionInfo.currentVersion)) {
if (versionInfo && versionInfo.releaseVersion && versionInfo.currentVersion) {
currentVersion.value = versionInfo.currentVersion
latestRelease.value = versionInfo.releaseVersion
} else {

100
packages/nc-gui/lang/ru.json

@ -284,17 +284,17 @@
"requestDataSource": "Request a data source you need?",
"apiKey": "API Key",
"sharedBase": "Shared Base",
"importData": "Import Data",
"importData": "Импорт данных",
"importSecondaryViews": "Import Secondary Views",
"importRollupColumns": "Import Rollup Columns",
"importLookupColumns": "Import Lookup Columns",
"importAttachmentColumns": "Import Attachment Columns",
"importFormulaColumns": "Import Formula Columns",
"noData": "No Data",
"goToDashboard": "Go to Dashboard",
"importing": "Importing",
"noData": "Нет данных",
"goToDashboard": "Перейти к панели управления",
"importing": "Импорт",
"flattenNested": "Flatten Nested",
"downloadAllowed": "Download allowed",
"downloadAllowed": "Скачивание разрешено",
"weAreHiring": "We are Hiring!",
"primaryKey": "Primary key",
"hasMany": "has many",
@ -302,13 +302,13 @@
"manyToMany": "have many to many relation",
"extraConnectionParameters": "Extra connection parameters",
"commentsOnly": "Comments only",
"documentation": "Documentation",
"subscribeNewsletter": "Subscribe to our weekly newsletter",
"signUpWithGoogle": "Sign up with Google",
"signInWithGoogle": "Sign in with Google",
"documentation": "Документация",
"subscribeNewsletter": "Подпишитесь на нашу еженедельную рассылку",
"signUpWithGoogle": "Зарегистрируйтесь с помощью Google",
"signInWithGoogle": "Войти при помощи Google",
"agreeToTos": "By signing up, you agree to the Terms of Service",
"welcomeToNc": "Welcome to NocoDB!",
"inviteOnlySignup": "Allow signup only using invite url"
"welcomeToNc": "Добро пожаловать в NocoDB!",
"inviteOnlySignup": "Разрешить регистрацию только по ссылке"
},
"activity": {
"createProject": "Создать проект",
@ -353,14 +353,14 @@
"invite": "Пригласить",
"inviteMore": "Пригласить еще",
"inviteTeam": "Пригласить команду",
"inviteUser": "Invite User",
"inviteUser": "Пригласить пользователя",
"inviteToken": "Токен приглашения",
"newUser": "Новый пользователь",
"editUser": "Редактировать пользователя",
"deleteUser": "Удалить пользователя из проекта",
"resendInvite": "Переотправить приглашение e-mail",
"copyInviteURL": "Скопировать URL-адрес приглашения",
"copyPasswordResetURL": "Copy password reset URL",
"copyPasswordResetURL": "Скопировать URL для сброса пароля",
"newRole": "Новая роль",
"reloadRoles": "Перезагрузить роли",
"nextPage": "Следущая страница",
@ -376,13 +376,13 @@
"setPrimary": "Установить в качестве основного значения",
"addRow": "Добавить новую строку",
"saveRow": "Сохранить строку",
"saveAndExit": "Save & Exit",
"saveAndStay": "Save & Stay",
"saveAndExit": "Сохранить и выйти",
"saveAndStay": "Сохранить и остаться",
"insertRow": "Вставить новый строк",
"deleteRow": "Удалить строку",
"deleteSelectedRow": "Удалить выбранные строки",
"importExcel": "Импорт из Excel",
"importCSV": "Import CSV",
"importCSV": "Импорт CSV",
"downloadCSV": "Скачать как CSV.",
"downloadExcel": "Скачать как XLSX",
"uploadCSV": "Загрузить CSV.",
@ -421,12 +421,12 @@
"editConnJson": "Редактировать соединение JSON",
"sponsorUs": "Спонсируйте нас",
"sendEmail": "Отправить письмо",
"addUserToProject": "Add user to project",
"addUserToProject": "Добавить пользователя в проект",
"getApiSnippet": "Get API Snippet",
"clearCell": "Clear cell",
"addFilterGroup": "Add Filter Group",
"clearCell": "Очистить ячейку",
"addFilterGroup": "Добавить группу фильтров",
"linkRecord": "Link record",
"addNewRecord": "Add new record",
"addNewRecord": "Добавить новую запись",
"useConnectionUrl": "Use Connection URL",
"toggleCommentsDraw": "Toggle comments draw",
"expandRecord": "Развернуть запись",
@ -647,44 +647,44 @@
},
"invalidURL": "Неверный URL",
"internalError": "Some internal error occurred",
"templateGeneratorNotFound": "Template Generator cannot be found!",
"fileUploadFailed": "Failed to upload file",
"templateGeneratorNotFound": "Генератор шаблонов не найден!",
"fileUploadFailed": "Не удалось загрузить файл",
"primaryColumnUpdateFailed": "Failed to update primary column",
"formDescriptionTooLong": "Data too long for Form Description",
"columnsRequired": "Following columns are required",
"selectAtleastOneColumn": "At least one column has to be selected",
"selectAtleastOneColumn": "Должен быть выбран как минимум один столбец",
"columnDescriptionNotFound": "Cannot find the destination column for",
"duplicateMappingFound": "Duplicate mapping found, please remove one of the mapping",
"nullValueViolatesNotNull": "Null value violates not-null constraint",
"sourceHasInvalidNumbers": "Source data contains some invalid numbers",
"sourceHasInvalidNumbers": "Исходные данные содержат недопустимые числа",
"sourceHasInvalidBoolean": "Source data contains some invalid boolean values",
"invalidForm": "Invalid Form",
"formValidationFailed": "Form validation failed",
"youHaveBeenSignedOut": "You have been signed out",
"failedToLoadList": "Failed to load list",
"formValidationFailed": "Ошибка проверки формы",
"youHaveBeenSignedOut": "Вы вышли из системы",
"failedToLoadList": "Не удалось загрузить список",
"failedToLoadChildrenList": "Failed to load children list",
"deleteFailed": "Delete failed",
"unlinkFailed": "Unlink failed",
"deleteFailed": "Не удалось удалить",
"unlinkFailed": "Не удалось отменить связь",
"rowUpdateFailed": "Row update failed",
"deleteRowFailed": "Failed to delete row",
"setFormDataFailed": "Failed to set form data",
"deleteRowFailed": "Не удалось удалить строку",
"setFormDataFailed": "Не удалось задать данные формы",
"formViewUpdateFailed": "Failed to update form view",
"tableNameRequired": "Table name is required",
"tableNameRequired": "Требуется имя таблицы",
"nameShouldStartWithAnAlphabetOr_": "Name should start with an alphabet or _",
"followingCharactersAreNotAllowed": "Following characters are not allowed",
"columnNameRequired": "Column name is required",
"projectNameExceeds50Characters": "Project name exceeds 50 characters",
"projectNameCannotStartWithSpace": "Project name cannot start with space",
"requiredField": "Required field",
"followingCharactersAreNotAllowed": "Нельзя использовать следующие символы",
"columnNameRequired": "Требуется название столбца",
"projectNameExceeds50Characters": "Название проекта превышает 50 символов",
"projectNameCannotStartWithSpace": "Название проекта не может начинаться с пробела",
"requiredField": "Обязательное поле",
"ipNotAllowed": "IP not allowed",
"targetFileIsNotAnAcceptedFileType": "Target file is not an accepted file type",
"theAcceptedFileTypeIsCsv": "The accepted file type is .csv",
"theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "The accepted file types are .xls, .xlsx, .xlsm, .ods, .ots",
"theAcceptedFileTypeIsCsv": "Допустимый тип файла: .csv",
"theAcceptedFileTypesAreXlsXlsxXlsmOdsOts": "Допустимые типы файлов: .xls, .xlsx, .xlsm, .ods, .ots",
"parameterKeyCannotBeEmpty": "Parameter key cannot be empty",
"duplicateParameterKeysAreNotAllowed": "Duplicate parameter keys are not allowed",
"fieldRequired": "{value} cannot be empty.",
"projectNotAccessible": "Project not accessible",
"copyToClipboardError": "Failed to copy to clipboard"
"fieldRequired": "{value} не может быть пустым.",
"projectNotAccessible": "Проект недоступен",
"copyToClipboardError": "Не удалось скопировать в буфер обмена"
},
"toast": {
"exportMetadata": "Метаданные проекта успешно экспортированы",
@ -704,18 +704,18 @@
"futureRelease": "Скоро!"
},
"success": {
"columnDuplicated": "Column duplicated successfully",
"columnDuplicated": "Столбец успешно скопирован",
"updatedUIACL": "Updated UI ACL for tables successfully",
"pluginUninstalled": "Plugin uninstalled successfully",
"pluginSettingsSaved": "Plugin settings saved successfully",
"pluginUninstalled": "Плагин успешно удален",
"pluginSettingsSaved": "Настройки плагина сохранены",
"pluginTested": "Successfully tested plugin settings",
"tableRenamed": "Table renamed successfully",
"viewDeleted": "View deleted successfully",
"tableRenamed": "Таблица успешно переименована",
"viewDeleted": "Представление успешно удалено",
"primaryColumnUpdated": "Successfully updated as primary column",
"tableDataExported": "Successfully exported all table data",
"updated": "Successfully updated",
"tableDataExported": "Все данные таблицы успешно экспортированы",
"updated": "Успешно обновлено",
"sharedViewDeleted": "Deleted shared view successfully",
"userDeleted": "User deleted successfully",
"userDeleted": "Пользователь успешно удален",
"viewRenamed": "View renamed successfully",
"tokenGenerated": "Token generated successfully",
"tokenDeleted": "Token deleted successfully",

3
packages/nc-gui/pages/[projectType]/[projectId]/index/index/index.vue

@ -19,7 +19,7 @@ const { isOverDropZone } = useDropZone(dropZone, onDrop)
const { files, open, reset } = useFileDialog()
const { isSharedBase } = useProject()
const { bases, isSharedBase } = useProject()
const { isUIAllowed } = useUIPermission()
@ -128,6 +128,7 @@ function openCreateTable() {
const { close } = useDialog(resolveComponent('DlgTableCreate'), {
'modelValue': isOpen,
'onUpdate:modelValue': closeDialog,
'baseId': bases.value[0].id,
})
function closeDialog() {

18
packages/noco-docs/content/en/engineering/builds-and-releases.md

@ -6,7 +6,9 @@ category: "Engineering"
menuTitle: "Releases & Builds"
---
## Builds of NocoDB
There are 3 kinds of docker builds in NocoDB
- Release builds [nocodb/nocodb](https://hub.docker.com/r/nocodb/nocodb) : built during NocoDB release.
- Daily builds [nocodb/nocodb-daily](https://hub.docker.com/r/nocodb/nocodb-daily) : built every 6 hours from Develop branch.
- Daily builds [nocodb/nocodb-timely](https://hub.docker.com/r/nocodb/nocodb-timely): built for every PR.
@ -26,12 +28,24 @@ Below is an overview of how to make these builds and what happens behind the sce
![image](https://user-images.githubusercontent.com/35857179/167240383-dda05f76-8323-4f4a-b3e7-9db886dbd68d.png)
- Then there would be two cases - you can either leave target tag and pervious tag blank or manually input some values
> Target Tag means the target deployment version, while Previous Tag means the latest version as of now. Previous Tag is used for Release Note only - showing the file / commit differences between two tags.
- Target Tag means the target deployment version, while Previous Tag means the latest version as of now. Previous Tag is used for Release Note only - showing the file / commit differences between two tags.
### Tagging
The naming convention would be following given the actual release tag is `0.100.0`
- `0.100.0-beta.1` (first version of pre-release)
- `0.100.0-beta.2` (include bug fix changes on top of the previous version)
- `0.100.0-beta.3`(include bug fix changes on top of the previous version)
- and so on ...
- `0.100.0` (actual release)
- `0.100.1` (minor bug fix release)
- `0.100.2` (minor bug fix release)
### Case 1: Leaving inputs blank
- If Previous Tag is blank, then the value will be fetched from [latest](https://github.com/nocodb/nocodb/releases/latest)
- If Target Tag is blank, then the value will be Previous Tag plus one. Example: 0.90.11 (Previous Tag) + 1 = 0.90.12 (Target Tag)
- If Target Tag is blank, then the value will be Previous Tag plus one. Example: 0.90.11 (Previous Tag) + 0.0.1 = 0.90.12 (Target Tag)
### Case 2: Manually Input

11
packages/nocodb/package-lock.json generated

@ -23,6 +23,7 @@
"bullmq": "^1.81.1",
"clear": "^0.1.0",
"colors": "1.4.0",
"compare-versions": "^5.0.1",
"cookie-parser": "^1.4.5",
"cors": "^2.8.5",
"cron": "^1.8.2",
@ -3810,6 +3811,11 @@
"integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
"dev": true
},
"node_modules/compare-versions": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-5.0.1.tgz",
"integrity": "sha512-v8Au3l0b+Nwkp4G142JcgJFh1/TUhdxut7wzD1Nq1dyp5oa3tXaqb03EXOAB6jS4gMlalkjAUPZBMiAfKUixHQ=="
},
"node_modules/component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
@ -20757,6 +20763,11 @@
"integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
"dev": true
},
"compare-versions": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-5.0.1.tgz",
"integrity": "sha512-v8Au3l0b+Nwkp4G142JcgJFh1/TUhdxut7wzD1Nq1dyp5oa3tXaqb03EXOAB6jS4gMlalkjAUPZBMiAfKUixHQ=="
},
"component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",

1
packages/nocodb/package.json

@ -63,6 +63,7 @@
"bullmq": "^1.81.1",
"clear": "^0.1.0",
"colors": "1.4.0",
"compare-versions": "^5.0.1",
"cookie-parser": "^1.4.5",
"cors": "^2.8.5",
"cron": "^1.8.2",

45
packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/formulav2/formulaQueryBuilderv2.ts

@ -8,6 +8,7 @@ import { XKnex } from '../../../index';
import LinkToAnotherRecordColumn from '../../../../../models/LinkToAnotherRecordColumn';
import LookupColumn from '../../../../../models/LookupColumn';
import { jsepCurlyHook, UITypes } from 'nocodb-sdk';
import { validateDateWithUnknownFormat } from '../helpers/formulaFnHelper';
// todo: switch function based on database
@ -55,8 +56,11 @@ export default async function formulaQueryBuilderv2(
jsep.plugins.register(jsepCurlyHook);
const tree = jsep(_tree);
const columnIdToUidt = {};
// todo: improve - implement a common solution for filter, sort, formula, etc
for (const col of await model.getColumns()) {
columnIdToUidt[col.id] = col.uidt;
if (col.id in aliasToColumn) continue;
switch (col.uidt) {
case UITypes.Formula:
@ -659,6 +663,47 @@ export default async function formulaQueryBuilderv2(
const right = fn(pt.right, null, pt.operator).toQuery();
let sql = `${left} ${pt.operator} ${right}${colAlias}`;
// comparing a date with empty string would throw
// `ERROR: zero-length delimited identifier` in Postgres
if (
knex.clientType() === 'pg' &&
columnIdToUidt[pt.left.name] === UITypes.Date
) {
// The correct way to compare with Date should be using
// `IS_AFTER`, `IS_BEFORE`, or `IS_SAME`
// This is to prevent empty data returned to UI due to incorrect SQL
if (pt.right.value === '') {
if (pt.operator === '=') {
sql = `${left} IS NULL ${colAlias}`;
} else {
sql = `${left} IS NOT NULL ${colAlias}`;
}
} else if (!validateDateWithUnknownFormat(pt.right.value)) {
// left tree value is date but right tree value is not date
// return true if left tree value is not null, else false
sql = `${left} IS NOT NULL ${colAlias}`;
}
}
if (
knex.clientType() === 'pg' &&
columnIdToUidt[pt.right.name] === UITypes.Date
) {
// The correct way to compare with Date should be using
// `IS_AFTER`, `IS_BEFORE`, or `IS_SAME`
// This is to prevent empty data returned to UI due to incorrect SQL
if (pt.left.value === '') {
if (pt.operator === '=') {
sql = `${right} IS NULL ${colAlias}`;
} else {
sql = `${right} IS NOT NULL ${colAlias}`;
}
} else if (!validateDateWithUnknownFormat(pt.left.value)) {
// right tree value is date but left tree value is not date
// return true if right tree value is not null, else false
sql = `${right} IS NOT NULL ${colAlias}`;
}
}
// handle NULL values when calling CONCAT for sqlite3
if (pt.left.fnName === 'CONCAT' && knex.clientType() === 'sqlite3') {
sql = `COALESCE(${left}, '') ${pt.operator} COALESCE(${right},'')${colAlias}`;

29
packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/helpers/formulaFnHelper.ts

@ -1,3 +1,7 @@
import dayjs, { extend } from 'dayjs';
import customParseFormat from 'dayjs/plugin/customParseFormat.js';
extend(customParseFormat);
export function getWeekdayByText(v: string) {
return {
monday: 0,
@ -21,3 +25,28 @@ export function getWeekdayByIndex(idx: number): string {
6: 'sunday',
}[idx || 0];
}
export function validateDateWithUnknownFormat(v: string) {
const dateFormats = [
'DD-MM-YYYY',
'MM-DD-YYYY',
'YYYY-MM-DD',
'DD/MM/YYYY',
'MM/DD/YYYY',
'YYYY/MM/DD',
'DD MM YYYY',
'MM DD YYYY',
'YYYY MM DD',
];
for (const format of dateFormats) {
if (dayjs(v, format, true).isValid() as any) {
return true;
}
for (const timeFormat of ['HH:mm', 'HH:mm:ss', 'HH:mm:ss.SSS']) {
if (dayjs(v, `${format} ${timeFormat}`, true).isValid() as any) {
return true;
}
}
}
return false;
}

22
packages/nocodb/src/lib/meta/api/utilApis.ts

@ -1,5 +1,6 @@
// // Project CRUD
import { Request, Response } from 'express';
import { compareVersions, validate } from 'compare-versions';
import { ViewTypes } from 'nocodb-sdk';
import Project from '../../models/Project';
@ -65,17 +66,22 @@ export async function versionInfo(_req: Request, res: Response) {
(versionCache.lastFetched &&
versionCache.lastFetched < Date.now() - 1000 * 60 * 60)
) {
versionCache.releaseVersion = await axios
.get('https://github.com/nocodb/nocodb/releases/latest', {
const nonBetaTags = await axios
.get('https://api.github.com/repos/nocodb/nocodb/tags', {
timeout: 5000,
})
.then((response) =>
response.request.res.responseUrl.replace(
'https://github.com/nocodb/nocodb/releases/tag/',
''
)
)
.then((response) => {
return response.data
.map((x) => x.name)
.filter(
(v) => validate(v) && !v.includes('finn') && !v.includes('beta')
)
.sort((x, y) => compareVersions(y, x));
})
.catch(() => null);
if (nonBetaTags && nonBetaTags.length > 0) {
versionCache.releaseVersion = nonBetaTags[0];
}
versionCache.lastFetched = Date.now();
}

Loading…
Cancel
Save