Skip to content

Commit

Permalink
Add CI/CD pull file filter mechanism
Browse files Browse the repository at this point in the history
  • Loading branch information
t-ski committed Nov 15, 2024
1 parent 7db4396 commit bfe67b0
Show file tree
Hide file tree
Showing 14 changed files with 212 additions and 99 deletions.
25 changes: 10 additions & 15 deletions packages/rjs-build/src/Plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,32 +62,27 @@ export class Plugin {

private resolveBuildModulePath(): string {
const buildConfig: TJSON = this.fetchBuildConfig();
const buildModuleReferences: string[] = _config.buildModuleNames.map(
(buildModuleName: string) =>
join(this.pluginDirectoryPath, buildModuleName)
);
buildConfig[_config.buildModuleReferenceKey] &&
buildModuleReferences.push(
resolve(
this.pluginDirectoryPath,
buildConfig[_config.buildModuleReferenceKey] as string
)
);
const buildModuleReferences: string[] = [
..._config.buildModuleNames.map((reference: string) =>
join(this.pluginDirectoryPath, reference)
),
buildConfig[_config.buildModuleReferenceKey] as string
].filter((reference: string | undefined) => !!reference);

let buildModulePath: string;
while (!buildModulePath && buildModuleReferences.length) {
const buildModuleReference: string = buildModuleReferences.shift();
try {
buildModulePath = require.resolve(buildModuleReference);
buildModulePath = require.resolve(buildModuleReference, {
paths: [this.pluginDirectoryPath]
});
buildModulePath = !/\.json$/.test(buildModulePath)
? buildModulePath
: null;
} catch {}
}

if (!buildModulePath || /\.json$/.test(buildModulePath)) return null;

return buildModulePath;
return buildModulePath ?? null;
}

private async fetchBuildInterface(): Promise<TPluginInterfaceCallable> {
Expand Down
1 change: 1 addition & 0 deletions packages/rjs-handler/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test/unit/.*.__repo
11 changes: 9 additions & 2 deletions packages/rjs-handler/src/Handler.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { resolve } from "path";
import { normalize, resolve } from "path";

import {
TAtomicSerializable,
Expand Down Expand Up @@ -41,13 +41,19 @@ export class Handler {
>;
private readonly rpcController: RPCController | null;
private readonly configuredHostnames: string[];
private readonly deployPaths: string[];

constructor(env: Partial<IHandlerEnv>, options: TJSON = {}) {
constructor(
env: Partial<IHandlerEnv>,
options: TJSON = {},
deployPaths: string[] = []
) {
this.env = new Options(env ?? {}, {
dev: false,
cwd: process.cwd()
}).object;
this.config = new TypeResolver(options ?? {}, GLOBAL_CONFIG_DEFAULTS);
this.deployPaths = deployPaths.map((path: string) => normalize(path));

this.configuredHostnames = [
"localhost",
Expand Down Expand Up @@ -223,6 +229,7 @@ export class Handler {
handler = new POSTHandlerContext(
request,
this.config,
this.deployPaths,
this.env.cwd,
this.env.dev
);
Expand Down
63 changes: 56 additions & 7 deletions packages/rjs-handler/src/handler-context/POSTHandlerContext.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { dirname, basename, join, resolve } from "path";
import { existsSync, readdirSync, writeFileSync, cpSync, rmSync } from "fs";
import { dirname, basename, join, resolve, normalize } from "path";
import {
existsSync,
readdirSync,
writeFileSync,
cpSync,
rmSync,
statSync,
mkdirSync
} from "fs";
import { IncomingMessage, OutgoingHttpHeaders } from "http";
import { request } from "https";
import { createHmac, timingSafeEqual } from "crypto";
Expand Down Expand Up @@ -37,11 +45,13 @@ export class POSTHandlerContext extends AHandlerContext {

private readonly cwd: string;
private readonly localEnv: LocalEnv;
private readonly deployPaths: string[];
private readonly repo: IGitRemote | null;

constructor(
request: Request,
config: TypeResolver,
deployPaths: string[],
cwd: string,
dev: boolean
) {
Expand Down Expand Up @@ -82,6 +92,7 @@ export class POSTHandlerContext extends AHandlerContext {

this.cwd = cwd;
this.localEnv = new LocalEnv(dev, cwd);
this.deployPaths = deployPaths ?? [];
}

private requestAPI(
Expand Down Expand Up @@ -221,11 +232,49 @@ export class POSTHandlerContext extends AHandlerContext {
force: true
});

readdirSync(dirPath).forEach((path: string) => {
cpSync(join(dirPath, path), join(this.cwd, path), {
force: true,
recursive: true
});
readdirSync(dirPath, {
recursive: true
}).forEach((rawPath: string | Buffer) => {
const path = rawPath.toString();

if (this.deployPaths.length) {
const maxDirectoryDepth = 50;

let isWhitelisted = false;
let i = 0;
let traversalPath: string = normalize(path);
do {
if (
this.deployPaths.includes(
normalize(traversalPath)
)
) {
isWhitelisted = true;

break;
}

traversalPath = normalize(
join(traversalPath, "..")
);
} while (
traversalPath !== "." &&
++i < maxDirectoryDepth
);

if (!isWhitelisted) return;
}

const sourcePath: string = join(dirPath, path);
const targetPath: string = join(this.cwd, path);
statSync(sourcePath).isDirectory()
? mkdirSync(targetPath, {
recursive: true
})
: cpSync(sourcePath, targetPath, {
force: true,
recursive: true
});
});

rmSync(dirPath, {
Expand Down
5 changes: 4 additions & 1 deletion packages/rjs-handler/test/unit/_api.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ const initHandler = (appWorkingDir) => {
},
"www": "never",
"hostnames": [ "example.org", "other.example.org" ]
});
}, [
"README.md",
"test/file.txt"
]);
}

const defaultHandler = initHandler(
Expand Down
65 changes: 42 additions & 23 deletions packages/rjs-handler/test/unit/webhook/webhook.test.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
const { createHmac } = require("crypto");

const { initHandler, requestWithHandler } = require("../_api");


const SECRET = "secret";
const PAYLOAD = JSON.stringify({
foo: "bar"
});


const requestWithWebhookHandler = () => {
return requestWithHandler(
initHandler(require("path").join(__dirname, "./app")),
Expand All @@ -16,6 +16,7 @@ const requestWithWebhookHandler = () => {
headers: {
"User-Agent": "GitHub-Hookshot/044aadd",
"X-Hub-Signature-256": `sha256=${
require("crypto").
createHmac("sha256", SECRET)
.update(PAYLOAD)
.digest("hex")
Expand All @@ -26,37 +27,55 @@ const requestWithWebhookHandler = () => {
);
};

const README_FILE_PATH = require("path").join(__dirname, "./app", "README.md");

const getFilePath = name => {
return require("path").join(__dirname, "./app", name);
}
const fileExists = name => {
return require("fs").existsSync(getFilePath(name));
}

const README_OVERRIDE_DATA = "OVERRIDE";
require("fs").writeFileSync(README_FILE_PATH, README_OVERRIDE_DATA);

require("fs").writeFileSync(getFilePath("README.md"), README_OVERRIDE_DATA);
require("fs").rmSync(getFilePath("EMPTY.md"), { force: true });
require("fs").rmSync(getFilePath("test/file-2.txt"), { force: true });


new UnitTest("POST_ GitHub:/ (dummy)")
.actual(async () => {
const res = await requestWithWebhookHandler();

const fileExists = name => {
return require("fs")
.existsSync(require("path").join(__dirname, "./app", name));
}

new UnitTest("POST GitHub:/ (dummy) → .env (from local)")
.actual(fileExists(".env"))
.expect(true);
await new Promise(r => setTimeout(() => {
new UnitTest("POST GitHub:/ (dummy) → .env (from local)")
.actual(fileExists(".env"))
.expect(true);
new UnitTest("POST GitHub:/ (dummy) → test/file.txt (from remote)")
.actual(fileExists("test/file.txt"))
.expect(true);

new UnitTest("POST GitHub:/ (dummy) test/file.txt (from remote)")
.actual(fileExists("test/file.txt"))
.expect(true);
new UnitTest("POST GitHub:/ (dummy) ¬ test/file-2.txt (non-whitelisted)")
.actual(fileExists("test/file-2.txt"))
.expect(false);

new UnitTest("POST GitHub:/ (dummy) ¬ non-existing.txt")
.actual(fileExists("non-existing.txt"))
.expect(false);

setTimeout(() => {
new UnitTest("POST GitHub:/ (dummy) ¬ EMPTY.md (non-whitelisted)")
.actual(fileExists("EMPTY.md"))
.expect(false);

new UnitTest("POST GitHub:/ (dummy) ¬ README.md override")
.actual(require("fs").readFileSync(README_FILE_PATH).toString().trim() !== README_OVERRIDE_DATA)
.actual(
require("fs")
.readFileSync(getFilePath("README.md"))
.toString()
.trim()
!== README_OVERRIDE_DATA
)
.expect(true);
}, 750); // TODO: Improve (reliability)


r();
}, 750)); // TODO: Improve reliability (wait for emit files visibility)

return res;
})
.expect({
Expand Down
15 changes: 10 additions & 5 deletions packages/rjs-server/src/Instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,33 @@ import _config from "./_config.json";
export function createInstance(
env?: Partial<IHandlerEnv>,
options?: TJSON,
deployPaths?: string[],
clusterSize?: IClusterConstraints
): Promise<Instance> {
return new Promise((resolve) => {
const instance: Instance = new Instance(env, options, clusterSize).on(
"online",
() => resolve(instance)
);
const instance: Instance = new Instance(
env,
options,
deployPaths,
clusterSize
).on("online", () => resolve(instance));
});
}

export class Instance extends Cluster<ISerialRequest, ISerialResponse> {
constructor(
env?: Partial<IHandlerEnv>,
options?: TJSON,
deployPaths?: string[],
clusterConstraints?: IClusterConstraints
) {
super(
{
modulePath: join(__dirname, "adapter"),
options: {
env,
options
options,
deployPaths
}
},
{
Expand Down
15 changes: 10 additions & 5 deletions packages/rjs-server/src/Server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,16 @@ export interface IServerEnv extends IHandlerEnv {
export function createServer(
env: IServerEnv,
options?: TJSON,
deployPaths?: string[],
clusterSize?: IClusterConstraints
): Promise<Server> {
return new Promise((resolve) => {
const server: Server = new Server(env, options, clusterSize).on(
"online",
() => resolve(server)
);
const server: Server = new Server(
env,
options,
deployPaths,
clusterSize
).on("online", () => resolve(server));
});
}

Expand All @@ -57,7 +60,7 @@ export class Server extends EventEmitter {
if (!param) return null;
if (param instanceof Buffer) return param;

const potentialPath: string = resolve(param);
const potentialPath: string = resolve(cwd, param);
if (!existsSync(potentialPath)) return param;

return readFileSync(potentialPath);
Expand Down Expand Up @@ -90,6 +93,7 @@ export class Server extends EventEmitter {
constructor(
env: IServerEnv,
options?: TJSON,
deployPaths?: string[],
clusterSize?: IClusterConstraints
) {
super();
Expand All @@ -102,6 +106,7 @@ export class Server extends EventEmitter {
this.instance = new Instance(
env,
options,
deployPaths,
this.env.dev
? {
processes: 1,
Expand Down
12 changes: 10 additions & 2 deletions packages/rjs-server/src/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,16 @@ import { ISerialRequest, ISerialResponse } from "./.shared/global.interfaces";

import { IHandlerEnv, Handler } from "@rapidjs.org/rjs-handler";

export default function (coreOptions: { env: IHandlerEnv; options: TJSON }) {
const handler: Handler = new Handler(coreOptions.env, coreOptions.options);
export default function (coreOptions: {
env: IHandlerEnv;
options: TJSON;
deployPaths: string[];
}) {
const handler: Handler = new Handler(
coreOptions.env,
coreOptions.options,
coreOptions.deployPaths
);
// TODO: Await for preretrieval done if not dev mode

return async (sReq: ISerialRequest): Promise<ISerialResponse> => {
Expand Down
2 changes: 1 addition & 1 deletion packages/rjs/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<p align="center">
<a href="https://rapidjs.org" target="_blank">
<img src="https://rapidjs.org/assets/img/repo-preview.jpg" alt="https://rapidjs.org" width="830">
<img src="https://rapidjs.org/assets/img/repo-preview.png" alt="https://rapidjs.org" width="830">
</a>
<h1 align="center">rJS</h1>
</p>
Expand Down
Loading

0 comments on commit bfe67b0

Please sign in to comment.