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: Enhance .gitignore handling using 'git ls-files' #16

Merged
merged 1 commit into from
Dec 22, 2023
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
3 changes: 1 addition & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
#!/usr/bin/env node

import { gitdir } from './lib/common';
import { remoteHelper } from './lib/remoteHelper';

// parse command line arguments
const [remoteName, remoteUrl] = process.argv.slice(2, 4); // only use 2 parametes (remoteName and repo url)

// get gitdir (usually '.git')
const gitdir = process.env.GIT_DIR as string;
if (!gitdir) throw new Error('Missing GIT_DIR env');

remoteHelper({
Expand Down
5 changes: 3 additions & 2 deletions src/lib/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@ export const PL_TMP_PATH = '.protocol.land';
export const GIT_CONFIG_KEYFILE = 'protocol.land.keyfile';
export const getWarpContractTxId = () =>
'w5ZU15Y2cLzZlu3jewauIlnzbKw-OAxbN9G5TbuuiDQ';
// get gitdir (usually '.git')
export const gitdir = process.env.GIT_DIR as string;

export const log = (message: any, options?: { color: 'red' | 'green' }) => {
if (!options) console.error(` [PL] ${message}`);
else {
const { color } = options;
console.error(
`${
color === 'red' ? ANSI_RED : ANSI_GREEN
`${color === 'red' ? ANSI_RED : ANSI_GREEN
} [PL] ${message}${ANSI_RESET}`
);
}
Expand Down
29 changes: 18 additions & 11 deletions src/lib/protocolLandSync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@ import { unpackGitRepo, zipRepoJsZip } from './zipHelper';
import type { Repo } from '../types';
import path from 'path';
import { existsSync } from 'fs';
import { PL_TMP_PATH, clearCache, getTags, isCacheDirty, log } from './common';
import {
PL_TMP_PATH,
clearCache,
getTags,
gitdir,
isCacheDirty,
log,
} from './common';

export const downloadProtocolLandRepo = async (
repoId: string,
Expand Down Expand Up @@ -106,22 +113,22 @@ export const uploadProtocolLandRepo = async (
repo: Repo,
destPath: string
) => {
// pack repo
log('Packing repo ...');
const buffer = await zipRepoJsZip(repo.name, repoPath, '', true, [
PL_TMP_PATH,
]);

// upload to bundlr/arweave
log('Uploading to Arweave ...');
let dataTxId: string | undefined;
try {
// pack repo
log('Packing repo ...');
const buffer = await zipRepoJsZip(repo.name, repoPath, '', [
path.join(gitdir, PL_TMP_PATH),
]);

// upload to bundlr/arweave
log('Uploading to Arweave ...');
dataTxId = await uploadRepo(
buffer,
await getTags(repo.name, repo.description)
);
} catch (error) {
log(error);
} catch (error: any) {
log(error?.message || error, { color: 'red' });
}
if (!dataTxId) return false;

Expand Down
96 changes: 55 additions & 41 deletions src/lib/zipHelper.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,18 @@
import fs from 'fs';
import { promises as fsPromises } from 'fs';
import path from 'path';
import JSZip from 'jszip';
import { log, waitFor } from './common';
import { exec } from 'child_process';
import { gitdir, log, waitFor } from './common';

export function writeBufferToFile(buffer: Buffer, filename: string) {
export async function writeBufferToFile(buffer: Buffer, filename: string) {
try {
fs.writeFileSync(filename, buffer);
await fsPromises.writeFile(filename, buffer);
log(`File "${filename}" written successfully.`);
} catch (error) {
log(`Error writing file: ${error}`);
}
}

function loadIgnoreList(rootPath: string) {
const gitignorePath = path.join(rootPath, '.gitignore');
if (fs.existsSync(gitignorePath)) {
const gitignoreContent = fs.readFileSync(gitignorePath, 'utf-8');
return gitignoreContent
.split('\n')
.map((line) => line.trim())
.filter((line) => line && !line.startsWith('#'));
}
return [];
}

export type UnpackGitRepoOptions = {
destPath: string;
arrayBuffer: ArrayBuffer;
Expand All @@ -34,70 +23,95 @@ export async function unpackGitRepo({
arrayBuffer,
}: UnpackGitRepoOptions) {
const zip = await JSZip.loadAsync(arrayBuffer);
const promises: any[] = [];

zip.forEach(async (_, file) => {
for (const [_, file] of Object.entries(zip.files)) {
if (file.dir) {
const folderPath = path.join(destPath, file.name);
fs.mkdirSync(folderPath, { recursive: true });
promises.push(fsPromises.mkdir(folderPath, { recursive: true }));
} else {
const filePath = path.join(destPath, file.name);
const content = await file.async('blob');
fs.writeFileSync(
filePath,
new Uint8Array(await content.arrayBuffer())
promises.push(
(async () => {
const filePath = path.join(destPath, file.name);

// Ensure the directory for the file exists
const dirPath = path.dirname(filePath);
await fsPromises.mkdir(dirPath, { recursive: true });

const content = await file.async('nodebuffer');
fsPromises.writeFile(filePath, content);
})()
);
}
});
}

await Promise.all(promises);

await waitFor(1000);

return true;
}

export async function getGitTrackedFiles() {
return new Promise<string[]>((resolve, reject) => {
exec('git ls-files', { encoding: 'utf-8' }, (error, stdout) => {
if (error) {
reject(new Error('Error getting git tracked files'));
} else {
resolve(stdout.trim().split('\n'));
}
});
});
}

export async function zipRepoJsZip(
mainPath: string,
zipRoot: string,
folderToZip?: string,
useGitignore?: boolean,
ignoreFiles?: string[]
) {
if (!folderToZip) folderToZip = zipRoot;

const ignoreList = useGitignore ? loadIgnoreList(zipRoot) : [];
const ignoreFilesList = ignoreFiles
? ignoreFiles.map((f) => path.join(zipRoot, f))
: [];

// build set of files to ignore from array argument + .gitignore if used
const ignoreSet = new Set([...ignoreList, ...ignoreFilesList]);

const zip = new JSZip();
const ignoreFilesList = ignoreFiles ?? [];

const filesToInclude: string[] = [];

// loop to walk the path to pack
const walk = (currentPath: string) => {
const items = fs.readdirSync(currentPath);
const walk = async (currentPath: string) => {
const items = await fsPromises.readdir(currentPath);

for (const item of items) {
const itemPath = path.join(currentPath, item);

if (ignoreSet.has(item)) {
if (
ignoreFilesList.some((ignorePath) =>
itemPath.startsWith(ignorePath)
)
) {
continue;
}

if (fs.statSync(itemPath).isDirectory()) {
walk(itemPath);
const stats = await fsPromises.stat(itemPath);

if (stats.isDirectory()) {
await walk(itemPath);
} else {
filesToInclude.push(itemPath);
}
}
};
walk(folderToZip);

await walk(gitdir);

const gitTrackedFiles = await getGitTrackedFiles();

filesToInclude.push(...gitTrackedFiles);

const zip = new JSZip();

// add files found to be included for packing
for (const file of filesToInclude) {
const content = fs.readFileSync(file);
const content = await fsPromises.readFile(file);
const relativePath = path.join(
mainPath ? mainPath + '/' : '',
path.relative(zipRoot, file)
Expand Down