mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
fix(core): Pick up release version and date from package.json (no-changelog) (#13666)
This commit is contained in:
parent
9ba9443460
commit
093cc982b8
1
.github/workflows/release-publish.yml
vendored
1
.github/workflows/release-publish.yml
vendored
|
@ -109,7 +109,6 @@ jobs:
|
||||||
context: ./docker/images/n8n
|
context: ./docker/images/n8n
|
||||||
build-args: |
|
build-args: |
|
||||||
N8N_VERSION=${{ needs.publish-to-npm.outputs.release }}
|
N8N_VERSION=${{ needs.publish-to-npm.outputs.release }}
|
||||||
N8N_RELEASE_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
provenance: false
|
provenance: false
|
||||||
push: true
|
push: true
|
||||||
|
|
|
@ -4,18 +4,15 @@ FROM n8nio/base:${NODE_VERSION}
|
||||||
ARG N8N_VERSION
|
ARG N8N_VERSION
|
||||||
RUN if [ -z "$N8N_VERSION" ] ; then echo "The N8N_VERSION argument is missing!" ; exit 1; fi
|
RUN if [ -z "$N8N_VERSION" ] ; then echo "The N8N_VERSION argument is missing!" ; exit 1; fi
|
||||||
|
|
||||||
ARG N8N_RELEASE_DATE
|
|
||||||
LABEL org.opencontainers.image.title="n8n"
|
LABEL org.opencontainers.image.title="n8n"
|
||||||
LABEL org.opencontainers.image.description="Workflow Automation Tool"
|
LABEL org.opencontainers.image.description="Workflow Automation Tool"
|
||||||
LABEL org.opencontainers.image.source="https://github.com/n8n-io/n8n"
|
LABEL org.opencontainers.image.source="https://github.com/n8n-io/n8n"
|
||||||
LABEL org.opencontainers.image.url="https://n8n.io"
|
LABEL org.opencontainers.image.url="https://n8n.io"
|
||||||
LABEL org.opencontainers.image.version=${N8N_VERSION}
|
LABEL org.opencontainers.image.version=${N8N_VERSION}
|
||||||
LABEL org.opencontainers.image.created=${N8N_RELEASE_DATE}
|
|
||||||
|
|
||||||
ENV N8N_VERSION=${N8N_VERSION}
|
ENV N8N_VERSION=${N8N_VERSION}
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
ENV N8N_RELEASE_TYPE=stable
|
ENV N8N_RELEASE_TYPE=stable
|
||||||
ENV N8N_RELEASE_DATE=${N8N_RELEASE_DATE}
|
|
||||||
RUN set -eux; \
|
RUN set -eux; \
|
||||||
npm install -g --omit=dev n8n@${N8N_VERSION} --ignore-scripts && \
|
npm install -g --omit=dev n8n@${N8N_VERSION} --ignore-scripts && \
|
||||||
npm rebuild --prefix=/usr/local/lib/node_modules/n8n sqlite3 && \
|
npm rebuild --prefix=/usr/local/lib/node_modules/n8n sqlite3 && \
|
||||||
|
|
|
@ -9,9 +9,6 @@ export class GenericConfig {
|
||||||
@Env('N8N_RELEASE_TYPE')
|
@Env('N8N_RELEASE_TYPE')
|
||||||
releaseChannel: 'stable' | 'beta' | 'nightly' | 'dev' = 'dev';
|
releaseChannel: 'stable' | 'beta' | 'nightly' | 'dev' = 'dev';
|
||||||
|
|
||||||
@Env('N8N_RELEASE_DATE')
|
|
||||||
releaseDate?: Date;
|
|
||||||
|
|
||||||
/** Grace period (in seconds) to wait for components to shut down before process exit. */
|
/** Grace period (in seconds) to wait for components to shut down before process exit. */
|
||||||
@Env('N8N_GRACEFUL_SHUTDOWN_TIMEOUT')
|
@Env('N8N_GRACEFUL_SHUTDOWN_TIMEOUT')
|
||||||
gracefulShutdownTimeout: number = 30;
|
gracefulShutdownTimeout: number = 30;
|
||||||
|
|
|
@ -10,14 +10,6 @@ export class SentryConfig {
|
||||||
@Env('N8N_FRONTEND_SENTRY_DSN')
|
@Env('N8N_FRONTEND_SENTRY_DSN')
|
||||||
frontendDsn: string = '';
|
frontendDsn: string = '';
|
||||||
|
|
||||||
/**
|
|
||||||
* Version of the n8n instance
|
|
||||||
*
|
|
||||||
* @example '1.73.0'
|
|
||||||
*/
|
|
||||||
@Env('N8N_VERSION')
|
|
||||||
n8nVersion: string = '';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Environment of the n8n instance.
|
* Environment of the n8n instance.
|
||||||
*
|
*
|
||||||
|
|
|
@ -243,7 +243,6 @@ describe('GlobalConfig', () => {
|
||||||
sentry: {
|
sentry: {
|
||||||
backendDsn: '',
|
backendDsn: '',
|
||||||
frontendDsn: '',
|
frontendDsn: '',
|
||||||
n8nVersion: '',
|
|
||||||
environment: '',
|
environment: '',
|
||||||
deploymentName: '',
|
deploymentName: '',
|
||||||
},
|
},
|
||||||
|
@ -326,7 +325,6 @@ describe('GlobalConfig', () => {
|
||||||
DB_LOGGING_MAX_EXECUTION_TIME: '0',
|
DB_LOGGING_MAX_EXECUTION_TIME: '0',
|
||||||
N8N_METRICS: 'TRUE',
|
N8N_METRICS: 'TRUE',
|
||||||
N8N_TEMPLATES_ENABLED: '0',
|
N8N_TEMPLATES_ENABLED: '0',
|
||||||
N8N_RELEASE_DATE: '2025-02-17T13:54:15Z',
|
|
||||||
};
|
};
|
||||||
const config = Container.get(GlobalConfig);
|
const config = Container.get(GlobalConfig);
|
||||||
expect(structuredClone(config)).toEqual({
|
expect(structuredClone(config)).toEqual({
|
||||||
|
@ -358,10 +356,6 @@ describe('GlobalConfig', () => {
|
||||||
...defaultConfig.templates,
|
...defaultConfig.templates,
|
||||||
enabled: false,
|
enabled: false,
|
||||||
},
|
},
|
||||||
generic: {
|
|
||||||
...defaultConfig.generic,
|
|
||||||
releaseDate: new Date('2025-02-17T13:54:15.000Z'),
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
expect(mockFs.readFileSync).not.toHaveBeenCalled();
|
expect(mockFs.readFileSync).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
@ -397,15 +391,4 @@ describe('GlobalConfig', () => {
|
||||||
'Invalid number value for DB_LOGGING_MAX_EXECUTION_TIME: abcd',
|
'Invalid number value for DB_LOGGING_MAX_EXECUTION_TIME: abcd',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle invalid timestamps', () => {
|
|
||||||
process.env = {
|
|
||||||
N8N_RELEASE_DATE: 'abcd',
|
|
||||||
};
|
|
||||||
const config = Container.get(GlobalConfig);
|
|
||||||
expect(config.generic.releaseDate).toBeUndefined();
|
|
||||||
expect(consoleWarnMock).toHaveBeenCalledWith(
|
|
||||||
'Invalid timestamp value for N8N_RELEASE_DATE: abcd',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,7 +14,13 @@ import { ensureError, sleep, UserError } from 'n8n-workflow';
|
||||||
|
|
||||||
import type { AbstractServer } from '@/abstract-server';
|
import type { AbstractServer } from '@/abstract-server';
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import { LICENSE_FEATURES, inDevelopment, inTest } from '@/constants';
|
import {
|
||||||
|
LICENSE_FEATURES,
|
||||||
|
N8N_VERSION,
|
||||||
|
N8N_RELEASE_DATE,
|
||||||
|
inDevelopment,
|
||||||
|
inTest,
|
||||||
|
} from '@/constants';
|
||||||
import * as CrashJournal from '@/crash-journal';
|
import * as CrashJournal from '@/crash-journal';
|
||||||
import * as Db from '@/db';
|
import * as Db from '@/db';
|
||||||
import { getDataDeduplicationService } from '@/deduplication';
|
import { getDataDeduplicationService } from '@/deduplication';
|
||||||
|
@ -63,15 +69,14 @@ export abstract class BaseCommand extends Command {
|
||||||
async init(): Promise<void> {
|
async init(): Promise<void> {
|
||||||
this.errorReporter = Container.get(ErrorReporter);
|
this.errorReporter = Container.get(ErrorReporter);
|
||||||
|
|
||||||
const { releaseDate } = this.globalConfig.generic;
|
const { backendDsn, environment, deploymentName } = this.globalConfig.sentry;
|
||||||
const { backendDsn, n8nVersion, environment, deploymentName } = this.globalConfig.sentry;
|
|
||||||
await this.errorReporter.init({
|
await this.errorReporter.init({
|
||||||
serverType: this.instanceSettings.instanceType,
|
serverType: this.instanceSettings.instanceType,
|
||||||
dsn: backendDsn,
|
dsn: backendDsn,
|
||||||
environment,
|
environment,
|
||||||
release: n8nVersion,
|
release: N8N_VERSION,
|
||||||
serverName: deploymentName,
|
serverName: deploymentName,
|
||||||
releaseDate,
|
releaseDate: N8N_RELEASE_DATE,
|
||||||
});
|
});
|
||||||
initExpressionEvaluator();
|
initExpressionEvaluator();
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { readFileSync } from 'fs';
|
import { readFileSync, statSync } from 'fs';
|
||||||
import type { n8n } from 'n8n-core';
|
import type { n8n } from 'n8n-core';
|
||||||
import type { ITaskDataConnections } from 'n8n-workflow';
|
import type { ITaskDataConnections } from 'n8n-workflow';
|
||||||
import { jsonParse, TRIMMED_TASK_DATA_CONNECTIONS_KEY } from 'n8n-workflow';
|
import { jsonParse, TRIMMED_TASK_DATA_CONNECTIONS_KEY } from 'n8n-workflow';
|
||||||
|
@ -18,9 +18,10 @@ export const TEMPLATES_DIR = join(CLI_DIR, 'templates');
|
||||||
export const NODES_BASE_DIR = dirname(require.resolve('n8n-nodes-base'));
|
export const NODES_BASE_DIR = dirname(require.resolve('n8n-nodes-base'));
|
||||||
export const EDITOR_UI_DIST_DIR = join(dirname(require.resolve('n8n-editor-ui')), 'dist');
|
export const EDITOR_UI_DIST_DIR = join(dirname(require.resolve('n8n-editor-ui')), 'dist');
|
||||||
|
|
||||||
export function getN8nPackageJson() {
|
const packageJsonPath = join(CLI_DIR, 'package.json');
|
||||||
return jsonParse<n8n.PackageJson>(readFileSync(join(CLI_DIR, 'package.json'), 'utf8'));
|
const n8nPackageJson = jsonParse<n8n.PackageJson>(readFileSync(packageJsonPath, 'utf8'));
|
||||||
}
|
export const N8N_VERSION = n8nPackageJson.version;
|
||||||
|
export const N8N_RELEASE_DATE = statSync(packageJsonPath).mtime;
|
||||||
|
|
||||||
export const STARTING_NODES = [
|
export const STARTING_NODES = [
|
||||||
'@n8n/n8n-nodes-langchain.manualChatTrigger',
|
'@n8n/n8n-nodes-langchain.manualChatTrigger',
|
||||||
|
@ -28,8 +29,6 @@ export const STARTING_NODES = [
|
||||||
'n8n-nodes-base.manualTrigger',
|
'n8n-nodes-base.manualTrigger',
|
||||||
];
|
];
|
||||||
|
|
||||||
export const N8N_VERSION = getN8nPackageJson().version;
|
|
||||||
|
|
||||||
export const NODE_PACKAGE_PREFIX = 'n8n-nodes-';
|
export const NODE_PACKAGE_PREFIX = 'n8n-nodes-';
|
||||||
|
|
||||||
export const STARTER_TEMPLATE_NAME = `${NODE_PACKAGE_PREFIX}starter`;
|
export const STARTER_TEMPLATE_NAME = `${NODE_PACKAGE_PREFIX}starter`;
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { InstanceSettings, Logger } from 'n8n-core';
|
||||||
import type { IWorkflowBase } from 'n8n-workflow';
|
import type { IWorkflowBase } from 'n8n-workflow';
|
||||||
|
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import { getN8nPackageJson, inDevelopment } from '@/constants';
|
import { inDevelopment, N8N_VERSION } from '@/constants';
|
||||||
import { isApiEnabled } from '@/public-api';
|
import { isApiEnabled } from '@/public-api';
|
||||||
import {
|
import {
|
||||||
ENV_VARS_DOCS_URL,
|
ENV_VARS_DOCS_URL,
|
||||||
|
@ -175,7 +175,7 @@ export class InstanceRiskReporter implements RiskReporter {
|
||||||
private async getOutdatedState() {
|
private async getOutdatedState() {
|
||||||
let versions = [];
|
let versions = [];
|
||||||
|
|
||||||
const localVersion = getN8nPackageJson().version;
|
const localVersion = N8N_VERSION;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
versions = await this.getNextVersions(localVersion).then((v) => this.removeIconData(v));
|
versions = await this.getNextVersions(localVersion).then((v) => this.removeIconData(v));
|
||||||
|
|
|
@ -114,9 +114,8 @@ export const MOCK_PACKAGE: InstalledPackages[] = [
|
||||||
export function simulateOutdatedInstanceOnce(versionName = MOCK_01110_N8N_VERSION.name) {
|
export function simulateOutdatedInstanceOnce(versionName = MOCK_01110_N8N_VERSION.name) {
|
||||||
const baseUrl = Container.get(GlobalConfig).versionNotifications.endpoint + '/';
|
const baseUrl = Container.get(GlobalConfig).versionNotifications.endpoint + '/';
|
||||||
|
|
||||||
jest
|
// @ts-expect-error readonly export
|
||||||
.spyOn(constants, 'getN8nPackageJson')
|
constants.N8N_VERSION = versionName;
|
||||||
.mockReturnValueOnce({ name: 'n8n', version: versionName });
|
|
||||||
|
|
||||||
nock(baseUrl).get(versionName).reply(200, [MOCK_01110_N8N_VERSION, MOCK_09990_N8N_VERSION]);
|
nock(baseUrl).get(versionName).reply(200, [MOCK_01110_N8N_VERSION, MOCK_09990_N8N_VERSION]);
|
||||||
}
|
}
|
||||||
|
@ -124,9 +123,8 @@ export function simulateOutdatedInstanceOnce(versionName = MOCK_01110_N8N_VERSIO
|
||||||
export function simulateUpToDateInstance(versionName = MOCK_09990_N8N_VERSION.name) {
|
export function simulateUpToDateInstance(versionName = MOCK_09990_N8N_VERSION.name) {
|
||||||
const baseUrl = Container.get(GlobalConfig).versionNotifications.endpoint + '/';
|
const baseUrl = Container.get(GlobalConfig).versionNotifications.endpoint + '/';
|
||||||
|
|
||||||
jest
|
// @ts-expect-error readonly export
|
||||||
.spyOn(constants, 'getN8nPackageJson')
|
constants.N8N_VERSION = versionName;
|
||||||
.mockReturnValueOnce({ name: 'n8n', version: versionName });
|
|
||||||
|
|
||||||
nock(baseUrl).persist().get(versionName).reply(200, [MOCK_09990_N8N_VERSION]);
|
nock(baseUrl).persist().get(versionName).reply(200, [MOCK_09990_N8N_VERSION]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,8 @@ type ErrorReporterInitOptions = {
|
||||||
beforeSendFilter?: (event: ErrorEvent, hint: EventHint) => boolean;
|
beforeSendFilter?: (event: ErrorEvent, hint: EventHint) => boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SIX_WEEKS_IN_MS = 6 * 7 * 24 * 60 * 60 * 1000;
|
const ONE_DAY_IN_MS = 24 * 60 * 60 * 1000;
|
||||||
|
const SIX_WEEKS_IN_MS = 6 * 7 * ONE_DAY_IN_MS;
|
||||||
const RELEASE_EXPIRATION_WARNING =
|
const RELEASE_EXPIRATION_WARNING =
|
||||||
'Error tracking disabled because this release is older than 6 weeks.';
|
'Error tracking disabled because this release is older than 6 weeks.';
|
||||||
|
|
||||||
|
@ -86,17 +87,23 @@ export class ErrorReporter {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (releaseDate) {
|
if (releaseDate) {
|
||||||
const releaseExpiresInMs = releaseDate.getTime() + SIX_WEEKS_IN_MS - Date.now();
|
const releaseExpiresAtMs = releaseDate.getTime() + SIX_WEEKS_IN_MS;
|
||||||
if (releaseExpiresInMs <= 0) {
|
const releaseExpiresInMs = () => releaseExpiresAtMs - Date.now();
|
||||||
|
if (releaseExpiresInMs() <= 0) {
|
||||||
this.logger.warn(RELEASE_EXPIRATION_WARNING);
|
this.logger.warn(RELEASE_EXPIRATION_WARNING);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Once this release expires, reject all events
|
const checkForExpiration = () => {
|
||||||
this.expirationTimer = setTimeout(() => {
|
// Once this release expires, reject all events
|
||||||
this.logger.warn(RELEASE_EXPIRATION_WARNING);
|
if (releaseExpiresInMs() <= 0) {
|
||||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
this.logger.warn(RELEASE_EXPIRATION_WARNING);
|
||||||
this.report = this.defaultReport;
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
}, releaseExpiresInMs);
|
this.report = this.defaultReport;
|
||||||
|
} else {
|
||||||
|
setTimeout(checkForExpiration, ONE_DAY_IN_MS);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
checkForExpiration();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dsn) return;
|
if (!dsn) return;
|
||||||
|
|
Loading…
Reference in a new issue