Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
export function formatBytes(bytes: number, options: FormattingOptions = {}): string {
const { units = BYTES_IN_KIBIBYTE, unitString = KIB_UNIT_STRING, formatter = defaultByteFormatting } = options;
return `${formatter(bytes, units).toLocaleString()} ${unitString}`;
}
export function formatSha(sha: string): string {
return sha.slice(0, 7);
}
function formatPercent(value: number): string {
return `${(value * 100).toFixed(3)}%`;
}
const levelToString = {
[BudgetLevel.WARN]: 'Warning',
[BudgetLevel.ERROR]: 'Error'
};
const levelToEmoji = {
[BudgetLevel.WARN]: '⚠️',
[BudgetLevel.ERROR]: '🚫'
};
export function formatBudgetResult(budgetResult: BudgetResult, itemName: string, useEmoji: boolean = false): string {
const { actual, expected, level, sizeKey, type } = budgetResult;
const actualFormatted = type === BudgetType.PERCENT_DELTA ? formatPercent(actual) : formatBytes(actual);
const expectedFormatted = type === BudgetType.PERCENT_DELTA ? formatPercent(expected) : formatBytes(expected);
const diffFormatted =
type === BudgetType.PERCENT_DELTA ? formatPercent(actual - expected) : formatBytes(actual - expected);
const prefix = `${(useEmoji ? levelToEmoji : levelToString)[level]}: \`${itemName}\``;
.sync(`${path.join(__dirname, '../src/fixtures/builds')}/*.json`)
.map(buildPath => ({ ...require(buildPath) }))
.sort((a, b) => b.meta.timestamp - a.meta.timestamp)
.forEach((build, i) => {
build.meta.timestamp = Math.floor(subDays(today, i).valueOf() / 1000);
builds.set(build.meta.revision.value || build.meta.revision, build);
});
module.exports = {
dev: true,
artifacts: {
groups: [
{
name: 'Entries',
artifactMatch: /^\w+$/,
budgets: [{ level: BudgetLevel.ERROR, sizeKey: 'gzip', type: BudgetType.SIZE, maximum: 250000 }]
},
{
name: 'Home',
artifactNames: ['main', 'vendor', 'shared', 'runtime', 'bundle.HomeTimeline'],
budgets: [{ level: BudgetLevel.ERROR, sizeKey: 'gzip', type: BudgetType.SIZE, maximum: 350000 }]
}
]
},
name: 'Static Fixtures',
queries: {
build: {
byRevision: async revision => {
return Promise.resolve(builds.get(revision));
},
insert: async () => {
throw new UnimplementedError();
/**
* To run a mysql docker container:
* docker run -p 3306:3306 --name bt-mysql -e MYSQL_ROOT_PASSWORD=tacos -e MYSQL_ROOT_HOST=% -e MYSQL_DATABASE=buildtracker -d mysql --default-authentication-plugin=mysql_native_password
* yarn ts-node src/server/src/index.ts setup -c ./config/mysql.js
* yarn ts-node src/server/src/index.ts seed -c ./config/mysql.js
*/
module.exports = withMysql({
defaultBranch: 'master',
dev: true,
artifacts: {
groups: [
{
name: 'Web App',
artifactMatch: /^app\/client/,
budgets: [{ level: BudgetLevel.ERROR, sizeKey: 'gzip', type: BudgetType.SIZE, maximum: 150000 }]
}
]
},
mysql: {
user: 'root',
password: 'tacos',
database: 'buildtracker',
host: '127.0.0.1',
port: 3306
},
url: 'http://localhost:3000'
});
const errorBudgets = failingBudgets.filter(budget => budget.level === BudgetLevel.ERROR);
const warningBudgets = failingBudgets.filter(budget => budget.level === BudgetLevel.WARN);
/**
* To run a mariadb docker container:
* docker run -p 3307:3306 --name bt-mariadb -e MYSQL_ROOT_PASSWORD=tacos -e MYSQL_ROOT_HOST=% -e MYSQL_DATABASE=buildtracker -d mariadb --default-authentication-plugin=mysql_native_password
* yarn ts-node src/server/src/index.ts setup -c ./config/mariadb.js
* yarn ts-node src/server/src/index.ts seed -c ./config/mariadb.js
*/
module.exports = withMaria({
defaultBranch: 'master',
dev: true,
artifacts: {
groups: [
{
name: 'Web App',
artifactMatch: /^app\/client/,
budgets: [{ level: BudgetLevel.ERROR, sizeKey: 'gzip', type: BudgetType.SIZE, maximum: 150000 }]
}
]
},
mariadb: {
user: 'root',
password: 'tacos',
database: 'buildtracker',
host: '127.0.0.1',
port: 3307
},
url: 'http://localhost:3000'
});
const errorFailingBudgets = cell.failingBudgets.some(result => result.level === BudgetLevel.ERROR);
const warningFailingBudgets = cell.failingBudgets.some(result => result.level === BudgetLevel.WARN);
/**
* To run a mysql docker container:
* docker run --name pg -e POSTGRES_PASSWORD=mysecretpassword -e POSTGRES_DB=buildtracker -p 54320:5432 -d postgres
* yarn ts-node src/server/src/index.ts setup -c ./config/postgres.js
* yarn ts-node src/server/src/index.ts seed -c ./config/postgres.js
*/
module.exports = withPostgres({
defaultBranch: 'master',
dev: true,
artifacts: {
groups: [
{
name: 'Web App',
artifactMatch: /^app\/client/,
budgets: [{ level: BudgetLevel.ERROR, sizeKey: 'gzip', type: BudgetType.SIZE, maximum: 150000 }]
}
]
},
pg: {
connectionString: 'postgresql://postgres:mysecretpassword@127.0.0.1:54320/buildtracker',
ssl: false
},
url: 'http://localhost:3000'
});
builds.set(build.meta.revision.value || build.meta.revision, build);
});
module.exports = {
dev: true,
artifacts: {
groups: [
{
name: 'Entries',
artifactMatch: /^\w+$/,
budgets: [{ level: BudgetLevel.ERROR, sizeKey: 'gzip', type: BudgetType.SIZE, maximum: 250000 }]
},
{
name: 'Home',
artifactNames: ['main', 'vendor', 'shared', 'runtime', 'bundle.HomeTimeline'],
budgets: [{ level: BudgetLevel.ERROR, sizeKey: 'gzip', type: BudgetType.SIZE, maximum: 350000 }]
}
]
},
name: 'Static Fixtures',
queries: {
build: {
byRevision: async revision => {
return Promise.resolve(builds.get(revision));
},
insert: async () => {
throw new UnimplementedError();
}
},
builds: {
byRevisions: async revisions => {
return Promise.resolve(revisions.map(revision => builds.get(revision)).filter(Boolean));
const withPostgres = require('@build-tracker/plugin-with-postgres').default;
const { BudgetLevel, BudgetType } = require('@build-tracker/types');
module.exports = withPostgres({
artifacts: {
groups: [
{
name: 'Web App',
artifactMatch: /^app\/client/,
budgets: [{ level: BudgetLevel.ERROR, sizeKey: 'gzip', type: BudgetType.SIZE, maximum: 150000 }]
}
]
},
defaultBranch: 'next',
pg: {
connectionString: process.env.DATABASE_URL,
ssl: true
},
port: process.env.PORT,
url: 'https://build-tracker-demo.herokuapp.com'
});
export function formatBudgetResult(budgetResult: BudgetResult, itemName: string, useEmoji: boolean = false): string {
const { actual, expected, level, sizeKey, type } = budgetResult;
const actualFormatted = type === BudgetType.PERCENT_DELTA ? formatPercent(actual) : formatBytes(actual);
const expectedFormatted = type === BudgetType.PERCENT_DELTA ? formatPercent(expected) : formatBytes(expected);
const diffFormatted =
type === BudgetType.PERCENT_DELTA ? formatPercent(actual - expected) : formatBytes(actual - expected);
const prefix = `${(useEmoji ? levelToEmoji : levelToString)[level]}: \`${itemName}\``;
switch (type) {
case BudgetType.DELTA:
return `${prefix} failed the ${sizeKey} budget delta limit. Expected to increase no more than ${expectedFormatted}, but increased by ${actualFormatted}`;
case BudgetType.PERCENT_DELTA:
return `${prefix} failed the ${sizeKey} budget percent change limit. Expected no increase by no more than ${expectedFormatted}, but increased by ${actualFormatted}`;
case BudgetType.SIZE:
return `${prefix} failed the ${sizeKey} budget size limit of ${expectedFormatted} by ${diffFormatted}`;
}
}