Skip to content

Commit

Permalink
fix: clean dirty cache + notify after failed push
Browse files Browse the repository at this point in the history
  • Loading branch information
7i7o committed Nov 20, 2023
1 parent 41443b7 commit d451f41
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 11 deletions.
5 changes: 5 additions & 0 deletions .changeset/real-ghosts-brake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@7i7o/git-remote-proland': patch
---

Fix dirty cache after failed upload to arweave
45 changes: 44 additions & 1 deletion src/lib/common.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import { getAddress } from './arweaveHelper';
import type { Repo, Tag } from '../types';
import { execSync } from 'child_process';
import { readFileSync } from 'fs';
import { accessSync, constants, readFileSync } from 'fs';
import type { JsonWebKey } from 'crypto';
import { emitKeypressEvents } from 'readline';
import path from 'path';

const ANSI_RESET = '\x1b[0m';
const ANSI_RED = '\x1b[31m';
const ANSI_GREEN = '\x1b[32m';

const DIRTY_EXT = ".tmp"

export const PL_TMP_PATH = '.protocol.land';
export const GIT_CONFIG_KEYFILE = 'protocol.land.keyfile';
export const getWarpContractTxId = () =>
Expand Down Expand Up @@ -121,6 +126,44 @@ export async function getTags(title: string, description: string) {
] as Tag[];
}

export function clearCache(
cachePath: string,
options: { keepFolders: string[] }
) {
const { keepFolders = [] } = options;
const ommitedFolders = keepFolders.map((v) => `! -name "${v}"`).join(' ');
execSync(
`find ${cachePath} -mindepth 1 -maxdepth 1 -type d ${ommitedFolders} -exec rm -rf {} \\;`
);
}

export function setCacheDirty(cachePath: string, remoteName: string) {
if (!cachePath || !remoteName)
throw new Error('Cache and MutexName are required');
execSync(`touch ${path.join(cachePath, remoteName, DIRTY_EXT)}`);
}

export function unsetCacheDirty(cachePath: string, remoteName: string) {
if (!cachePath || !remoteName)
throw new Error('Cache and MutexName are required');
execSync(`rm -f ${path.join(cachePath, remoteName, DIRTY_EXT)}`);
}

export function isCacheDirty(cachePath: string, remoteName: string) {
if (!cachePath || !remoteName)
throw new Error('Cache and MutexName are required');
// Check if the file exists
try {
accessSync(
path.join(cachePath, remoteName, DIRTY_EXT),
constants.R_OK | constants.W_OK
);
return true;
} catch {
return false;
}
}

export const waitFor = (delay: number) =>
new Promise((res) => setTimeout(res, delay));

Expand Down
19 changes: 12 additions & 7 deletions src/lib/protocolLandSync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { Repo } from '../types';
import path from 'path';
import { defaultCacheOptions } from 'warp-contracts/mjs';
import { existsSync } from 'fs';
import { PL_TMP_PATH, getTags, log } from './common';
import { PL_TMP_PATH, clearCache, getTags, isCacheDirty, log } from './common';

export const downloadProtocolLandRepo = async (
repoId: string,
Expand Down Expand Up @@ -34,10 +34,17 @@ export const downloadProtocolLandRepo = async (
// use tmp folder named as repo's latest dataTxId
const latestVersionRepoPath = path.join(destPath, repo.dataTxId);

// if folder exsits, assume it's cached and return
// if folder exists, there is a cached remote
if (existsSync(latestVersionRepoPath)) {
log(`Using cached repo in '${latestVersionRepoPath}'`);
return repo;
// check if cache is dirty
if (!isCacheDirty(destPath, repo.dataTxId)) {
// cache is clean, use it
log(`Using cached repo in '${latestVersionRepoPath}'`);
return repo;
}

// cache is dirty, clear cache and continue
clearCache(destPath, { keepFolders: ['cache'] });
}

// if not, download repo data from arweave
Expand Down Expand Up @@ -89,9 +96,7 @@ export const downloadProtocolLandRepo = async (

// rm -rf everything but the bare repo and warp cache (discard stdout)
try {
execSync(
`find ${destPath} -mindepth 1 -maxdepth 1 -type d ! -name "cache" ! -name "${repo.dataTxId}" -exec rm -rf {} \\;`
);
clearCache(destPath, { keepFolders: ['cache', repo.dataTxId] });
} catch {}

return repo;
Expand Down
27 changes: 24 additions & 3 deletions src/lib/remoteHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ import path from 'path';
import type { Repo } from '../types';
import {
PL_TMP_PATH,
getJwkPath,
clearCache,
getWallet,
log,
ownerOrContributor,
setCacheDirty,
unsetCacheDirty,
waitFor,
walletNotFoundMessage,
} from './common';
Expand Down Expand Up @@ -174,12 +176,15 @@ const spawnPipedGitCommand = async (
process.exit(code ? code : 1);
}

// if pushed to tmp bare remote ok and objects were updated, then upload repo to protocol land
// if pushed to tmp bare remote ok AND objects were updated, then upload repo to protocol land
if (gitCommand === 'git-receive-pack' && objectsUpdated) {
log(
`Push to temp remote finished successfully, now syncing with Protocol Land ...`
);

// mark cache as inconsistent
setCacheDirty(tmpPath, repo.dataTxId);

const pathToPack = path.join(remoteUrl, '..', '..', '..');

waitFor(1000);
Expand All @@ -190,14 +195,30 @@ const spawnPipedGitCommand = async (
tmpPath
);

// We clear the cached remote:
// If upload succeeded, there's a new txId for the repo
// If upload failed, the cached remote has an inconsistent state
clearCache(tmpPath, { keepFolders: ['cache'] });

// remove inconsistent cache mark
unsetCacheDirty(tmpPath, repo.dataTxId);

if (success)
log(`Successfully pushed repo '${repo.id}' to Protocol Land`, {
color: 'green',
});
else
else {
log(`Failed to push repo '${repo.id}' to Protocol Land`, {
color: 'red',
});
log(
'Please run `git pull` first to clean the cache and integrate your changes',
{
color: 'red',
}
);
process.exit(1);
}
}
});
};

0 comments on commit d451f41

Please sign in to comment.