Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix performance regression in scripts/bun/checkEnvironmentSymlinks.mts #1

Merged
merged 6 commits into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/publishWorkflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ jobs:
build:
runs-on: ubuntu-22.04
steps:
- name: CPU Details
run: lscpu
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v1
with:
bun-version: "1.0.28"
bun-version: "1.0.29"
- uses: mangs/simple-release-notes-action@v2
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pullRequestWorkflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v1
with:
bun-version: "1.0.28"
bun-version: "1.0.29"
- run: bun install --frozen-lockfile
- run: bun run check:environment
- run: bun --bun run check:formatting
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## 1.0.1

- Fix performance regression in `scripts/bun/checkEnvironmentSymlinks.mts`
- Bun target version updated to `1.0.29`
- Change the pull request template to be non-empty
- Update dependency versions to latest

## 1.0.0

- First public release
Binary file modified bun.lockb
Binary file not shown.
8 changes: 8 additions & 0 deletions config/github/pullRequestTemplate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
**Pull Request Checklist**

- [ ] Readme and changelog updates were made reflecting this PR's changes
- [ ] Increase the project version number in [`package.json`](/package.json) following [Semantic Versioning](http://semver.org/)

**Changes Included**

- REPLACE_ME
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"name": "@mangs/bun-utils",
"version": "1.0.0",
"version": "1.0.1",
"author": "Eric L. Goldstein",
"description": "Useful utils for your Bun projects",
"engines": {
"bun": "1.0.28"
"bun": "1.0.29"
},
"packageManager": "[email protected].28",
"packageManager": "[email protected].29",
"files": [
"./scripts",
"./utils"
Expand Down Expand Up @@ -45,12 +45,12 @@
"reinstall:use-lock-file": "bun run --silent delete:node-modules && bun install --frozen-lockfile"
},
"dependencies": {
"type-fest": "4.10.2",
"type-fest": "4.10.3",
"yoctocolors": "1.0.0"
},
"devDependencies": {
"@babbel/eslint-config": "1.2.1",
"@types/bun": "1.0.6",
"@babbel/eslint-config": "1.2.2",
"@types/bun": "1.0.7",
"eslint": "8.56.0",
"eslint-config-prettier": "9.1.0",
"prettier": "3.2.5",
Expand Down
36 changes: 25 additions & 11 deletions scripts/bun/checkEnvironmentSymlinks.mts
Original file line number Diff line number Diff line change
@@ -1,40 +1,54 @@
#!/usr/bin/env bun

// External Imports
import { lstat } from 'node:fs/promises';
import { realpathSync } from 'node:fs';
import { lstat, realpath } from 'node:fs/promises';
import { relative } from 'node:path';

// Internal Imports
import { getFilesRecursive } from '../../utils/filesystemUtils.mts';
import { getPerformanceLabel, printError, printSuccess } from '../../utils/consoleUtils.mts';

// Local Types
interface PathError {
path: string;
}

// Local Functions
async function main() {
const startTime = Bun.nanoseconds();

const brokenSymlinkPaths = [];
const filePaths = getFilesRecursive('.');
const filePaths = await getFilesRecursive('.');
const fileStatList = await Promise.all(filePaths.map((path) => lstat(path)));
const pathPromises: Promise<string>[] = [];
for (const [index, fileStats] of fileStatList.entries()) {
if (fileStats.isSymbolicLink()) {
const path = filePaths[index]!;
try {
realpathSync(path);
} catch {
brokenSymlinkPaths.push(path);
}
pathPromises.push(
realpath(path).catch((error: PathError) => {
// eslint-disable-next-line @typescript-eslint/no-throw-literal -- simplified logic to extract broken symlink file paths
throw error.path;
}),
);
}
}

const brokenSymlinkPaths = [];
for (const result of await Promise.allSettled(pathPromises)) {
if (result.status === 'rejected') {
brokenSymlinkPaths.push(result.reason as string);
}
}

const performanceLabel = getPerformanceLabel(startTime);
if (brokenSymlinkPaths.length > 0) {
process.exitCode = 1;
printError('The following symlinks point to a non-existant file:');
for (const path of brokenSymlinkPaths) {
printError(`\t- ${path}`);
printError(`\t- ${relative('.', path)}`);
}
printError(performanceLabel);
return;
}
const performanceLabel = getPerformanceLabel(startTime);

// Success!!!
printSuccess(`Environment symlinks valid ${performanceLabel}`);
Expand Down
4 changes: 2 additions & 2 deletions scripts/bun/checkEnvironmentVersions.mts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

// External Imports
import { access, readdir } from 'node:fs/promises';
import fs, { readFileSync } from 'node:fs';
import fs from 'node:fs';

// Internal Imports
import { getPerformanceLabel, printError, printSuccess } from '../../utils/consoleUtils.mts';
Expand All @@ -15,7 +15,7 @@ const filePaths = {
githubWorkflowsDirectory: './.github/workflows/',
packageJson: './package.json',
} as const;
const packageJson = JSON.parse(readFileSync('./package.json', { encoding: 'utf8' })) as PackageJson;
const packageJson = JSON.parse(await Bun.file('./package.json').text()) as PackageJson;

// Local Types
interface EnvironmentVersions {
Expand Down
4 changes: 2 additions & 2 deletions scripts/git/installGitHooks.mts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ async function main() {
return;
}

const isDestinationPathAccessible = isDirectoryAccessible(gitHooksDestinationPath);
const isDestinationPathAccessible = await isDirectoryAccessible(gitHooksDestinationPath);
if (!isDestinationPathAccessible) {
// Skip silently because there is no .git directory, thus likely executing inside node_modules
return;
}
const isSourcePathAccessible = isDirectoryAccessible(gitHooksSourcePath);
const isSourcePathAccessible = await isDirectoryAccessible(gitHooksSourcePath);
if (!isSourcePathAccessible) {
const { name: packageName } = await import('../../package.json', { with: { type: 'json' } });
printError(
Expand Down
2 changes: 1 addition & 1 deletion scripts/git/removeGitHooks.mts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ async function main() {
return;
}

const isPathAccessible = isDirectoryAccessible(gitHooksPath);
const isPathAccessible = await isDirectoryAccessible(gitHooksPath);
if (!isPathAccessible) {
// Skip silently because there is no .git directory, thus likely executing inside node_modules
return;
Expand Down
31 changes: 18 additions & 13 deletions utils/filesystemUtils.mts
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
// External Imports
import { access, readdir } from 'node:fs/promises';
import { resolve } from 'node:path';
import fs, { accessSync, readdirSync } from 'node:fs';
import fs from 'node:fs';

// Local Variables
const fileSizeUnits = ['B', 'KiB', 'MiB', 'GiB'] as const;

// Local Functions
function getFilesRecursive(targetDirectory: string, ignore = ['node_modules']): string[] {
async function getFilesRecursive(
targetDirectory: string,
ignore = ['node_modules'],
): Promise<string[]> {
const directoryPathAbsolute = resolve(targetDirectory);
const directoryEntries = readdirSync(directoryPathAbsolute, { withFileTypes: true });
const recursiveEntries = directoryEntries.map((entry) => {
if (ignore.includes(entry.name)) {
return '';
}
const pathAbsolute = resolve(directoryPathAbsolute, entry.name);
return entry.isDirectory() ? getFilesRecursive(pathAbsolute) : pathAbsolute;
});
const directoryEntries = await readdir(directoryPathAbsolute, { withFileTypes: true });
const recursiveEntries = await Promise.all(
directoryEntries.map((entry) => {
if (ignore.includes(entry.name)) {
return '';
}
const pathAbsolute = resolve(directoryPathAbsolute, entry.name);
return entry.isDirectory() ? getFilesRecursive(pathAbsolute) : pathAbsolute;
}),
);
return recursiveEntries.flat(Number.POSITIVE_INFINITY).filter(Boolean) as string[];
}

Expand All @@ -35,10 +40,10 @@ function getHumanReadableFilesize(filesize: number) {
return `${localizedSize} ${fileSizeUnits[divisionCount]}`;
}

function isDirectoryAccessible(path: string) {
async function isDirectoryAccessible(path: string) {
try {
// eslint-disable-next-line no-bitwise -- following the documentation for fs constants: https://nodejs.org/docs/latest-v16.x/api/fs.html#fspromisesaccesspath-mode
accessSync(path, fs.constants.R_OK | fs.constants.X_OK);
await access(path, fs.constants.R_OK | fs.constants.X_OK);
return true;
} catch {
return false;
Expand Down