Updates
This commit is contained in:
parent
cee4bc0478
commit
619afc8303
39
README.md
39
README.md
@ -4,7 +4,7 @@ NOTE: This package is for Bun runtime
|
||||
|
||||
Integrate a simple CI/CD process into your application without the hassle.
|
||||
|
||||
_**NOTE:** This package needs `node` installed to work_
|
||||
_**NOTE:** This package needs `bun` installed to work_
|
||||
|
||||
## Requirements
|
||||
|
||||
@ -43,7 +43,7 @@ Your `buncid.config.json` file should look like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"start": "node index.js",
|
||||
"start": "bun index.ts",
|
||||
"preflight": ["bun run test", "bun run build"]
|
||||
}
|
||||
```
|
||||
@ -52,7 +52,7 @@ or
|
||||
|
||||
```json
|
||||
{
|
||||
"start": "node index.js",
|
||||
"start": "bun index.ts",
|
||||
"preflight": "./preflight.sh"
|
||||
}
|
||||
```
|
||||
@ -63,7 +63,7 @@ Optionally you could include a `redeploy_path` in your config file:
|
||||
|
||||
```json
|
||||
{
|
||||
"start": "node index.js",
|
||||
"start": "bun index.ts",
|
||||
"preflight": "./preflight.sh",
|
||||
"redeploy_path": "./REDEPLOY"
|
||||
}
|
||||
@ -75,7 +75,7 @@ You can change the name and path of the `redeploy_path`, just make sure the path
|
||||
|
||||
```json
|
||||
{
|
||||
"start": "node index.js",
|
||||
"start": "bun index.ts",
|
||||
"preflight": "./preflight.sh",
|
||||
"redeploy_path": "./deploy/trigger.txt"
|
||||
}
|
||||
@ -104,3 +104,32 @@ This app just runs whatever command you send it in an isolated child process, th
|
||||
### Redeployment
|
||||
|
||||
For continuos deployment and integration there needs to be a text file located in your project which the application can watch. Any time the content of this file is changed the application will rebuild and rerun your `start` command.
|
||||
|
||||
## Rebuilds
|
||||
|
||||
`buncid` provides rebuild scripts for popular frameworks
|
||||
|
||||
### Next JS
|
||||
|
||||
use `bunx buncid-builds-next` or simply `buncid-builds-next` if you installed globally, to rebuild a next js app incrementally. This has a few requirements though.
|
||||
|
||||
#### Update your `next-config.js` file
|
||||
|
||||
You need to update the distribution directory in your `next-config.ts`. Like this:
|
||||
|
||||
```javascript
|
||||
import type { NextConfig } from "next";
|
||||
import grabDist from "@moduletrace/buncid/dist/rebuilds/next-js/grabDist";
|
||||
|
||||
const distDir = grabDist();
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
/* config options here */
|
||||
reactStrictMode: true,
|
||||
distDir,
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
```
|
||||
|
||||
That's it. This dynamically handles your distribution directory for both `dev` and `start` scripts. Your `development` environment uses the `.next` directory, while your `production` environment uses the `.dist` directory.
|
||||
|
83
buncid.ts
Executable file
83
buncid.ts
Executable file
@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import colors from "./utils/console-colors";
|
||||
import startProcess from "./utils/start";
|
||||
import type { NodeCIConfig } from "./types";
|
||||
|
||||
const WORK_DIR = process.cwd();
|
||||
|
||||
declare global {
|
||||
var REDEPLOYMENTS: number;
|
||||
}
|
||||
|
||||
global.REDEPLOYMENTS = 0;
|
||||
|
||||
function run() {
|
||||
try {
|
||||
const configText = fs.readFileSync(
|
||||
path.join(WORK_DIR, "buncid.config.json"),
|
||||
"utf-8"
|
||||
);
|
||||
|
||||
const config: NodeCIConfig = JSON.parse(configText);
|
||||
|
||||
const {
|
||||
start,
|
||||
preflight,
|
||||
postflight,
|
||||
build,
|
||||
redeploy_path,
|
||||
first_run,
|
||||
port,
|
||||
} = config;
|
||||
|
||||
let redeployFile: string | undefined;
|
||||
|
||||
if (!redeploy_path) {
|
||||
const defaultRedeployPath = path.join(WORK_DIR, "REDEPLOY");
|
||||
const checkExistingPath = fs.existsSync(defaultRedeployPath);
|
||||
|
||||
if (!checkExistingPath) {
|
||||
fs.writeFileSync(
|
||||
defaultRedeployPath,
|
||||
Date.now().toString(),
|
||||
"utf-8"
|
||||
);
|
||||
}
|
||||
|
||||
redeployFile = path.join(WORK_DIR, "REDEPLOY");
|
||||
} else {
|
||||
redeployFile = path.resolve(WORK_DIR, redeploy_path);
|
||||
}
|
||||
|
||||
if (!redeployFile)
|
||||
throw new Error("Redeploy file variable not provided!");
|
||||
if (!fs.existsSync(redeployFile))
|
||||
throw new Error("Redeploy file not found!");
|
||||
|
||||
startProcess({
|
||||
command: start,
|
||||
preflight,
|
||||
redeploy_file: redeployFile,
|
||||
first_run,
|
||||
port,
|
||||
postflight,
|
||||
});
|
||||
} catch (error: any) {
|
||||
console.log(
|
||||
`${colors.FgRed}ERROR:${colors.Reset} CI process failed! => ${error.message}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
run();
|
||||
|
||||
process.on("exit", () => {
|
||||
console.log("Process exiting ...");
|
||||
});
|
||||
|
||||
process.on("beforeExit", () => {
|
||||
console.log("Process Before exit ...");
|
||||
});
|
5
dist/buncid.d.ts
vendored
Normal file
5
dist/buncid.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bun
|
||||
declare global {
|
||||
var REDEPLOYMENTS: number;
|
||||
}
|
||||
export {};
|
53
dist/buncid.js
vendored
Normal file
53
dist/buncid.js
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env bun
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const fs_1 = __importDefault(require("fs"));
|
||||
const path_1 = __importDefault(require("path"));
|
||||
const console_colors_1 = __importDefault(require("./utils/console-colors"));
|
||||
const start_1 = __importDefault(require("./utils/start"));
|
||||
const WORK_DIR = process.cwd();
|
||||
global.REDEPLOYMENTS = 0;
|
||||
function run() {
|
||||
try {
|
||||
const configText = fs_1.default.readFileSync(path_1.default.join(WORK_DIR, "buncid.config.json"), "utf-8");
|
||||
const config = JSON.parse(configText);
|
||||
const { start, preflight, postflight, build, redeploy_path, first_run, port, } = config;
|
||||
let redeployFile;
|
||||
if (!redeploy_path) {
|
||||
const defaultRedeployPath = path_1.default.join(WORK_DIR, "REDEPLOY");
|
||||
const checkExistingPath = fs_1.default.existsSync(defaultRedeployPath);
|
||||
if (!checkExistingPath) {
|
||||
fs_1.default.writeFileSync(defaultRedeployPath, Date.now().toString(), "utf-8");
|
||||
}
|
||||
redeployFile = path_1.default.join(WORK_DIR, "REDEPLOY");
|
||||
}
|
||||
else {
|
||||
redeployFile = path_1.default.resolve(WORK_DIR, redeploy_path);
|
||||
}
|
||||
if (!redeployFile)
|
||||
throw new Error("Redeploy file variable not provided!");
|
||||
if (!fs_1.default.existsSync(redeployFile))
|
||||
throw new Error("Redeploy file not found!");
|
||||
(0, start_1.default)({
|
||||
command: start,
|
||||
preflight,
|
||||
redeploy_file: redeployFile,
|
||||
first_run,
|
||||
port,
|
||||
postflight,
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
console.log(`${console_colors_1.default.FgRed}ERROR:${console_colors_1.default.Reset} CI process failed! => ${error.message}`);
|
||||
}
|
||||
}
|
||||
run();
|
||||
process.on("exit", () => {
|
||||
console.log("Process exiting ...");
|
||||
});
|
||||
process.on("beforeExit", () => {
|
||||
console.log("Process Before exit ...");
|
||||
});
|
14
dist/index.d.ts
vendored
14
dist/index.d.ts
vendored
@ -1,5 +1,9 @@
|
||||
#!/usr/bin/env bun
|
||||
declare global {
|
||||
var REDEPLOYMENTS: number;
|
||||
}
|
||||
export {};
|
||||
import grabDist from "./rebuilds/next-js/grabDist";
|
||||
declare const buncid: {
|
||||
builds: {
|
||||
nextJs: {
|
||||
grabDist: typeof grabDist;
|
||||
};
|
||||
};
|
||||
};
|
||||
export default buncid;
|
||||
|
55
dist/index.js
vendored
55
dist/index.js
vendored
@ -1,51 +1,14 @@
|
||||
#!/usr/bin/env bun
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const fs_1 = __importDefault(require("fs"));
|
||||
const path_1 = __importDefault(require("path"));
|
||||
const console_colors_1 = __importDefault(require("./utils/console-colors"));
|
||||
const start_1 = __importDefault(require("./utils/start"));
|
||||
const WORK_DIR = process.cwd();
|
||||
global.REDEPLOYMENTS = 0;
|
||||
function run() {
|
||||
try {
|
||||
const configText = fs_1.default.readFileSync(path_1.default.join(WORK_DIR, "buncid.config.json"), "utf-8");
|
||||
const config = JSON.parse(configText);
|
||||
const { start, preflight, postflight, build, redeploy_path, first_run, port, } = config;
|
||||
let redeployFile;
|
||||
if (!redeploy_path) {
|
||||
const defaultRedeployPath = path_1.default.join(WORK_DIR, "REDEPLOY");
|
||||
const checkExistingPath = fs_1.default.existsSync(defaultRedeployPath);
|
||||
if (!checkExistingPath) {
|
||||
fs_1.default.writeFileSync(defaultRedeployPath, Date.now().toString(), "utf-8");
|
||||
}
|
||||
redeployFile = path_1.default.join(WORK_DIR, "REDEPLOY");
|
||||
}
|
||||
else {
|
||||
redeployFile = path_1.default.resolve(WORK_DIR, redeploy_path);
|
||||
}
|
||||
if (!redeployFile)
|
||||
throw new Error("Redeploy file not found!");
|
||||
(0, start_1.default)({
|
||||
command: start,
|
||||
preflight,
|
||||
redeploy_file: redeployFile,
|
||||
first_run,
|
||||
port,
|
||||
postflight,
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
console.log(`${console_colors_1.default.FgRed}ERROR:${console_colors_1.default.Reset} CI process failed! => ${error.message}`);
|
||||
}
|
||||
}
|
||||
run();
|
||||
process.on("exit", () => {
|
||||
console.log("Process exiting ...");
|
||||
});
|
||||
process.on("beforeExit", () => {
|
||||
console.log("Process Before exit ...");
|
||||
});
|
||||
const grabDist_1 = __importDefault(require("./rebuilds/next-js/grabDist"));
|
||||
const buncid = {
|
||||
builds: {
|
||||
nextJs: {
|
||||
grabDist: grabDist_1.default,
|
||||
},
|
||||
},
|
||||
};
|
||||
exports.default = buncid;
|
||||
|
6
dist/rebuilds/next-js/grabDist.d.ts
vendored
Normal file
6
dist/rebuilds/next-js/grabDist.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
/**
|
||||
* # Grab the current distribution directory
|
||||
* @description This returns the relative path from the CWD. Eg `./.dist/build-1`
|
||||
* @returns {string | undefined}
|
||||
*/
|
||||
export default function grabDist(): string | undefined;
|
45
dist/rebuilds/next-js/grabDist.js
vendored
Normal file
45
dist/rebuilds/next-js/grabDist.js
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.default = grabDist;
|
||||
const fs_1 = __importDefault(require("fs"));
|
||||
const path_1 = __importDefault(require("path"));
|
||||
const production = process.env.NODE_ENV == "production";
|
||||
const isBuilding = process.env.BUILDING_APP;
|
||||
/**
|
||||
* # Grab the current distribution directory
|
||||
* @description This returns the relative path from the CWD. Eg `./.dist/build-1`
|
||||
* @returns {string | undefined}
|
||||
*/
|
||||
function grabDist() {
|
||||
const DIST_DIR = path_1.default.resolve(process.cwd(), "./.dist");
|
||||
if (isBuilding) {
|
||||
const distDir = (() => {
|
||||
try {
|
||||
const buildNumber = fs_1.default.readFileSync(`${DIST_DIR}/BUILD`, "utf-8");
|
||||
return `.dist/build-${buildNumber}`;
|
||||
}
|
||||
catch ( /** @type {*} */error) {
|
||||
console.log("Build Number Generation Error =>", error.message);
|
||||
process.exit();
|
||||
}
|
||||
})();
|
||||
return distDir;
|
||||
}
|
||||
if (production) {
|
||||
const distDir = (() => {
|
||||
try {
|
||||
const buildNumber = fs_1.default.readFileSync(`${DIST_DIR}/BUILD`, "utf-8");
|
||||
return `.dist/build-${buildNumber}`;
|
||||
}
|
||||
catch ( /** @type {*} */error) {
|
||||
console.log("Build Number Parse Error =>", error.message);
|
||||
process.exit();
|
||||
}
|
||||
})();
|
||||
return distDir;
|
||||
}
|
||||
return undefined;
|
||||
}
|
2
dist/rebuilds/next-js/index.d.ts
vendored
Normal file
2
dist/rebuilds/next-js/index.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env bun
|
||||
export {};
|
113
dist/rebuilds/next-js/index.js
vendored
Normal file
113
dist/rebuilds/next-js/index.js
vendored
Normal file
@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env bun
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
var _a;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const path_1 = __importDefault(require("path"));
|
||||
const fs_1 = __importDefault(require("fs"));
|
||||
const child_process_1 = require("child_process");
|
||||
const DIST_DIR = path_1.default.resolve(process.cwd(), "./.dist");
|
||||
let PREV_BUILD_NO = "0";
|
||||
const MAX_BUILDS = process.env.BUNCID_MAX_BUILDS
|
||||
? Number(process.env.BUNCID_MAX_BUILDS)
|
||||
: 10;
|
||||
if (MAX_BUILDS < 1 ||
|
||||
Number.isNaN(MAX_BUILDS) ||
|
||||
typeof MAX_BUILDS !== "number") {
|
||||
throw new Error("Invalid MAX_BUILDS");
|
||||
}
|
||||
if (!fs_1.default.existsSync(DIST_DIR))
|
||||
fs_1.default.mkdirSync(DIST_DIR);
|
||||
if (fs_1.default.existsSync(`${DIST_DIR}/BUILD`)) {
|
||||
PREV_BUILD_NO = fs_1.default.readFileSync(`${DIST_DIR}/BUILD`, "utf-8");
|
||||
}
|
||||
else {
|
||||
fs_1.default.writeFileSync(`${DIST_DIR}/BUILD`, "0", "utf-8");
|
||||
}
|
||||
try {
|
||||
const buildNumber = fs_1.default.readFileSync(`${DIST_DIR}/BUILD`, "utf-8");
|
||||
const newBuildNumber = Number(buildNumber) + 1;
|
||||
if (newBuildNumber < 0) {
|
||||
throw new Error("Invalid Build Number");
|
||||
}
|
||||
fs_1.default.writeFileSync(`${DIST_DIR}/BUILD`, String(newBuildNumber));
|
||||
if (newBuildNumber > MAX_BUILDS) {
|
||||
const builds = fs_1.default.readdirSync(DIST_DIR);
|
||||
const buildDirs = builds.filter((build) => build.match(/build-\d+/));
|
||||
for (const buildDir of buildDirs) {
|
||||
const buildDirPath = path_1.default.join(DIST_DIR, buildDir);
|
||||
const buildDirStat = fs_1.default.statSync(buildDirPath);
|
||||
if (buildDirStat.isDirectory()) {
|
||||
const buildDirName = buildDir.split("-")[1];
|
||||
const buildDirNumber = Number(buildDirName);
|
||||
if (buildDirNumber <= newBuildNumber - MAX_BUILDS) {
|
||||
fs_1.default.rmdirSync(buildDirPath, { recursive: true });
|
||||
console.log("Deleted Build Directory =>", buildDirPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.log("Build Number Parse Error =>", error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
const spawnSyncOptions = {
|
||||
stdio: "inherit",
|
||||
encoding: "utf-8",
|
||||
shell: ((_a = process.platform) === null || _a === void 0 ? void 0 : _a.match(/win32/i)) ? "bash.exe" : undefined,
|
||||
env: Object.assign(Object.assign({}, process.env), { BUILDING_APP: "true" }),
|
||||
};
|
||||
const build = (0, child_process_1.spawnSync)("bunx", ["next", "build"], spawnSyncOptions);
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
function grabNewDistDir() {
|
||||
try {
|
||||
const buildNumber = fs_1.default.readFileSync(`${DIST_DIR}/BUILD`, "utf-8");
|
||||
return `.dist/build-${buildNumber}`;
|
||||
}
|
||||
catch ( /** @type {*} */error) {
|
||||
console.log("Build Number Parse Error =>", error.message);
|
||||
process.exit();
|
||||
}
|
||||
}
|
||||
const newDistDir = grabNewDistDir();
|
||||
// if (!fs.existsSync(newDistDir)) {
|
||||
// fs.mkdirSync(newDistDir, { recursive: true });
|
||||
// }
|
||||
// if (!fs.existsSync(`${newDistDir}/BUILD_ID`)) {
|
||||
// fs.writeFileSync(`${newDistDir}/BUILD_ID`, "4");
|
||||
// }
|
||||
/**
|
||||
* # Revert Directories
|
||||
* @param {string} dir - New Build Directory Path
|
||||
*/
|
||||
function revert(dir) {
|
||||
var _a;
|
||||
console.log("Build Failed!", ((_a = build === null || build === void 0 ? void 0 : build.error) === null || _a === void 0 ? void 0 : _a.message) || build.stderr);
|
||||
fs_1.default.writeFileSync(`${DIST_DIR}/BUILD`, PREV_BUILD_NO, "utf-8");
|
||||
(0, child_process_1.execSync)(`rm -Rf ${dir}`, { cwd: process.cwd() });
|
||||
const writeErr = build.error
|
||||
? build.error.message
|
||||
: build.stderr
|
||||
? build.stderr.toString()
|
||||
: "NO BUILD_ID found in New Build Folder";
|
||||
fs_1.default.writeFileSync(`${DIST_DIR}/LAST_BUILD_FAIL`, Date() + "\n\n" + writeErr, "utf-8");
|
||||
process.exit(1);
|
||||
}
|
||||
if (build.error ||
|
||||
build.stderr ||
|
||||
build.status != 0 ||
|
||||
!fs_1.default.existsSync(`${newDistDir}/BUILD_ID`)) {
|
||||
revert(newDistDir);
|
||||
throw new Error("Build Failed!");
|
||||
}
|
||||
process.on("exit", () => {
|
||||
const onExitDir = grabNewDistDir();
|
||||
if (!fs_1.default.existsSync(`${onExitDir}/BUILD_ID`)) {
|
||||
revert(onExitDir);
|
||||
}
|
||||
});
|
6
dist/test/next-app/my-app/functions/grabDist.d.ts
vendored
Normal file
6
dist/test/next-app/my-app/functions/grabDist.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
/**
|
||||
* # Grab the current distribution directory
|
||||
* @description This returns the relative path from the CWD. Eg `./.dist/build-1`
|
||||
* @returns {string | undefined}
|
||||
*/
|
||||
export default function grabDist(): string | undefined;
|
45
dist/test/next-app/my-app/functions/grabDist.js
vendored
Normal file
45
dist/test/next-app/my-app/functions/grabDist.js
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.default = grabDist;
|
||||
const fs_1 = __importDefault(require("fs"));
|
||||
const path_1 = __importDefault(require("path"));
|
||||
const production = process.env.NODE_ENV == "production";
|
||||
const isBuilding = process.env.BUILDING_APP;
|
||||
/**
|
||||
* # Grab the current distribution directory
|
||||
* @description This returns the relative path from the CWD. Eg `./.dist/build-1`
|
||||
* @returns {string | undefined}
|
||||
*/
|
||||
function grabDist() {
|
||||
const DIST_DIR = path_1.default.resolve(process.cwd(), "./.dist");
|
||||
if (isBuilding) {
|
||||
const distDir = (() => {
|
||||
try {
|
||||
const buildNumber = fs_1.default.readFileSync(`${DIST_DIR}/BUILD`, "utf-8");
|
||||
return `.dist/build-${buildNumber}`;
|
||||
}
|
||||
catch ( /** @type {*} */error) {
|
||||
console.log("Build Number Generation Error =>", error.message);
|
||||
process.exit();
|
||||
}
|
||||
})();
|
||||
return distDir;
|
||||
}
|
||||
if (production) {
|
||||
const distDir = (() => {
|
||||
try {
|
||||
const buildNumber = fs_1.default.readFileSync(`${DIST_DIR}/BUILD`, "utf-8");
|
||||
return `.dist/build-${buildNumber}`;
|
||||
}
|
||||
catch ( /** @type {*} */error) {
|
||||
console.log("Build Number Parse Error =>", error.message);
|
||||
process.exit();
|
||||
}
|
||||
})();
|
||||
return distDir;
|
||||
}
|
||||
return undefined;
|
||||
}
|
3
dist/test/next-app/my-app/next.config.d.ts
vendored
Normal file
3
dist/test/next-app/my-app/next.config.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
import type { NextConfig } from "next";
|
||||
declare const nextConfig: NextConfig;
|
||||
export default nextConfig;
|
13
dist/test/next-app/my-app/next.config.js
vendored
Normal file
13
dist/test/next-app/my-app/next.config.js
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const grabDist_1 = __importDefault(require("./functions/grabDist"));
|
||||
const distDir = (0, grabDist_1.default)();
|
||||
const nextConfig = {
|
||||
/* config options here */
|
||||
reactStrictMode: true,
|
||||
distDir,
|
||||
};
|
||||
exports.default = nextConfig;
|
6
dist/test/next-app/my-app/pages/api/hello.d.ts
vendored
Normal file
6
dist/test/next-app/my-app/pages/api/hello.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
type Data = {
|
||||
name: string;
|
||||
};
|
||||
export default function handler(req: NextApiRequest, res: NextApiResponse<Data>): void;
|
||||
export {};
|
6
dist/test/next-app/my-app/pages/api/hello.js
vendored
Normal file
6
dist/test/next-app/my-app/pages/api/hello.js
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.default = handler;
|
||||
function handler(req, res) {
|
||||
res.status(200).json({ name: "John Doe" });
|
||||
}
|
13
dist/test/next-app/my-app/tailwind.config.d.ts
vendored
Normal file
13
dist/test/next-app/my-app/tailwind.config.d.ts
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
declare const _default: {
|
||||
content: string[];
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
background: string;
|
||||
foreground: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
plugins: never[];
|
||||
};
|
||||
export default _default;
|
18
dist/test/next-app/my-app/tailwind.config.js
vendored
Normal file
18
dist/test/next-app/my-app/tailwind.config.js
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.default = {
|
||||
content: [
|
||||
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
background: "var(--background)",
|
||||
foreground: "var(--foreground)",
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
2
dist/tsconfig.tsbuildinfo
vendored
2
dist/tsconfig.tsbuildinfo
vendored
File diff suppressed because one or more lines are too long
17
dist/utils/kill-child.js
vendored
17
dist/utils/kill-child.js
vendored
@ -27,15 +27,18 @@ function killChild(childProcess, port) {
|
||||
try {
|
||||
const childProcessPID = childProcess.pid;
|
||||
childProcess.kill();
|
||||
if (typeof port == "object" && (port === null || port === void 0 ? void 0 : port[0])) {
|
||||
for (let i = 0; i < port.length; i++) {
|
||||
const singlePort = port[i];
|
||||
yield (0, kill_port_1.default)(Number(singlePort));
|
||||
try {
|
||||
if (typeof port == "object" && (port === null || port === void 0 ? void 0 : port[0])) {
|
||||
for (let i = 0; i < port.length; i++) {
|
||||
const singlePort = port[i];
|
||||
yield (0, kill_port_1.default)(Number(singlePort));
|
||||
}
|
||||
}
|
||||
else if (port) {
|
||||
yield (0, kill_port_1.default)(Number(port));
|
||||
}
|
||||
}
|
||||
else if (port) {
|
||||
yield (0, kill_port_1.default)(Number(port));
|
||||
}
|
||||
catch (error) { }
|
||||
return true;
|
||||
}
|
||||
catch (error) {
|
||||
|
2
dist/utils/start.d.ts
vendored
2
dist/utils/start.d.ts
vendored
@ -10,7 +10,7 @@
|
||||
*/
|
||||
export default function startProcess({ command, preflight, postflight, redeploy_file, port, first_run, }: {
|
||||
command: string;
|
||||
preflight: string[] | string;
|
||||
preflight?: string[] | string;
|
||||
postflight?: string[] | string;
|
||||
redeploy_file: string;
|
||||
port?: string | number | (string | number)[];
|
||||
|
7
dist/utils/start.js
vendored
7
dist/utils/start.js
vendored
@ -26,11 +26,7 @@ function startProcess({ command, preflight, postflight, redeploy_file, port, fir
|
||||
try {
|
||||
if (first_run) {
|
||||
console.log("First Run ...");
|
||||
const runPreflight = (0, preflight_1.default)(preflight);
|
||||
}
|
||||
if (!preflight) {
|
||||
console.log(`${console_colors_1.default.FgRed}Error:${console_colors_1.default.Reset} No preflight included in config file. If you don't want to run any preflight command simply add an empty array.`);
|
||||
process.exit();
|
||||
const runFirstPreflight = (0, preflight_1.default)(preflight);
|
||||
}
|
||||
childProcess = (0, run_1.default)(command);
|
||||
if (!childProcess) {
|
||||
@ -50,7 +46,6 @@ function startProcess({ command, preflight, postflight, redeploy_file, port, fir
|
||||
try {
|
||||
const runPreflight = (0, preflight_1.default)(preflight);
|
||||
if (!runPreflight) {
|
||||
// TODO: Action to take if preflight fails
|
||||
console.log(`${console_colors_1.default.FgRed}Error:${console_colors_1.default.Reset} Preflight Failed.`);
|
||||
}
|
||||
else {
|
||||
|
87
index.ts
87
index.ts
@ -1,80 +1,11 @@
|
||||
#!/usr/bin/env bun
|
||||
import grabDist from "./rebuilds/next-js/grabDist";
|
||||
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import colors from "./utils/console-colors";
|
||||
import startProcess from "./utils/start";
|
||||
import type { NodeCIConfig } from "./types";
|
||||
const buncid = {
|
||||
builds: {
|
||||
nextJs: {
|
||||
grabDist,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const WORK_DIR = process.cwd();
|
||||
|
||||
declare global {
|
||||
var REDEPLOYMENTS: number;
|
||||
}
|
||||
|
||||
global.REDEPLOYMENTS = 0;
|
||||
|
||||
function run() {
|
||||
try {
|
||||
const configText = fs.readFileSync(
|
||||
path.join(WORK_DIR, "buncid.config.json"),
|
||||
"utf-8"
|
||||
);
|
||||
|
||||
const config: NodeCIConfig = JSON.parse(configText);
|
||||
|
||||
const {
|
||||
start,
|
||||
preflight,
|
||||
postflight,
|
||||
build,
|
||||
redeploy_path,
|
||||
first_run,
|
||||
port,
|
||||
} = config;
|
||||
|
||||
let redeployFile: string | undefined;
|
||||
|
||||
if (!redeploy_path) {
|
||||
const defaultRedeployPath = path.join(WORK_DIR, "REDEPLOY");
|
||||
const checkExistingPath = fs.existsSync(defaultRedeployPath);
|
||||
|
||||
if (!checkExistingPath) {
|
||||
fs.writeFileSync(
|
||||
defaultRedeployPath,
|
||||
Date.now().toString(),
|
||||
"utf-8"
|
||||
);
|
||||
}
|
||||
|
||||
redeployFile = path.join(WORK_DIR, "REDEPLOY");
|
||||
} else {
|
||||
redeployFile = path.resolve(WORK_DIR, redeploy_path);
|
||||
}
|
||||
|
||||
if (!redeployFile) throw new Error("Redeploy file not found!");
|
||||
|
||||
startProcess({
|
||||
command: start,
|
||||
preflight,
|
||||
redeploy_file: redeployFile,
|
||||
first_run,
|
||||
port,
|
||||
postflight,
|
||||
});
|
||||
} catch (error: any) {
|
||||
console.log(
|
||||
`${colors.FgRed}ERROR:${colors.Reset} CI process failed! => ${error.message}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
run();
|
||||
|
||||
process.on("exit", () => {
|
||||
console.log("Process exiting ...");
|
||||
});
|
||||
|
||||
process.on("beforeExit", () => {
|
||||
console.log("Process Before exit ...");
|
||||
});
|
||||
export default buncid;
|
||||
|
@ -1,10 +1,11 @@
|
||||
{
|
||||
"name": "@moduletrace/buncid",
|
||||
"version": "1.0.3",
|
||||
"version": "1.0.4",
|
||||
"description": "Simple CI/CD process For Bun runtime",
|
||||
"main": "dist/index.js",
|
||||
"bin": {
|
||||
"buncid": "./dist/index.js"
|
||||
"buncid": "./dist/buncid.js",
|
||||
"buncid-builds-next": "./dist/rebuilds/next-js/index.js"
|
||||
},
|
||||
"scripts": {
|
||||
"compile": "bun build --compile --minify --sourcemap --bytecode index.ts --outfile bin/buncid"
|
||||
|
28
rebuilds/next-js/README.md
Normal file
28
rebuilds/next-js/README.md
Normal file
@ -0,0 +1,28 @@
|
||||
# Rebuild your next project incrementally.
|
||||
|
||||
Create builds incrementally without stopping your server.
|
||||
|
||||
## Requirements
|
||||
|
||||
There are a few requirements to get this to work
|
||||
|
||||
### Update your `next-config.js` file
|
||||
|
||||
You need to update the distribution directory in your `next-config.js`. Like this:
|
||||
|
||||
```javascript
|
||||
import type { NextConfig } from "next";
|
||||
import grabDist from "@moduletrace/buncid/dist/rebuilds/next-js/grabDist";
|
||||
|
||||
const distDir = grabDist();
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
/* config options here */
|
||||
reactStrictMode: true,
|
||||
distDir,
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
```
|
||||
|
||||
That's it. This dynamically handles your distribution directory for both `dev` and `start` scripts. Your `development` environment uses the `.next` directory, while your `production` environment uses the `.dist` directory.
|
50
rebuilds/next-js/grabDist.ts
Normal file
50
rebuilds/next-js/grabDist.ts
Normal file
@ -0,0 +1,50 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
const production = process.env.NODE_ENV == "production";
|
||||
const isBuilding = process.env.BUILDING_APP;
|
||||
|
||||
/**
|
||||
* # Grab the current distribution directory
|
||||
* @description This returns the relative path from the CWD. Eg `./.dist/build-1`
|
||||
* @returns {string | undefined}
|
||||
*/
|
||||
export default function grabDist(): string | undefined {
|
||||
const DIST_DIR = path.resolve(process.cwd(), "./.dist");
|
||||
|
||||
if (isBuilding) {
|
||||
const distDir = (() => {
|
||||
try {
|
||||
const buildNumber = fs.readFileSync(
|
||||
`${DIST_DIR}/BUILD`,
|
||||
"utf-8"
|
||||
);
|
||||
return `.dist/build-${buildNumber}`;
|
||||
} catch (/** @type {*} */ error: any) {
|
||||
console.log("Build Number Generation Error =>", error.message);
|
||||
process.exit();
|
||||
}
|
||||
})();
|
||||
|
||||
return distDir;
|
||||
}
|
||||
|
||||
if (production) {
|
||||
const distDir = (() => {
|
||||
try {
|
||||
const buildNumber = fs.readFileSync(
|
||||
`${DIST_DIR}/BUILD`,
|
||||
"utf-8"
|
||||
);
|
||||
return `.dist/build-${buildNumber}`;
|
||||
} catch (/** @type {*} */ error: any) {
|
||||
console.log("Build Number Parse Error =>", error.message);
|
||||
process.exit();
|
||||
}
|
||||
})();
|
||||
|
||||
return distDir;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
139
rebuilds/next-js/index.ts
Normal file
139
rebuilds/next-js/index.ts
Normal file
@ -0,0 +1,139 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import {
|
||||
execSync,
|
||||
spawnSync,
|
||||
SpawnSyncOptionsWithStringEncoding,
|
||||
} from "child_process";
|
||||
|
||||
const DIST_DIR = path.resolve(process.cwd(), "./.dist");
|
||||
let PREV_BUILD_NO = "0";
|
||||
|
||||
const MAX_BUILDS = process.env.BUNCID_MAX_BUILDS
|
||||
? Number(process.env.BUNCID_MAX_BUILDS)
|
||||
: 10;
|
||||
|
||||
if (
|
||||
MAX_BUILDS < 1 ||
|
||||
Number.isNaN(MAX_BUILDS) ||
|
||||
typeof MAX_BUILDS !== "number"
|
||||
) {
|
||||
throw new Error("Invalid MAX_BUILDS");
|
||||
}
|
||||
|
||||
if (!fs.existsSync(DIST_DIR)) fs.mkdirSync(DIST_DIR);
|
||||
|
||||
if (fs.existsSync(`${DIST_DIR}/BUILD`)) {
|
||||
PREV_BUILD_NO = fs.readFileSync(`${DIST_DIR}/BUILD`, "utf-8");
|
||||
} else {
|
||||
fs.writeFileSync(`${DIST_DIR}/BUILD`, "0", "utf-8");
|
||||
}
|
||||
|
||||
try {
|
||||
const buildNumber = fs.readFileSync(`${DIST_DIR}/BUILD`, "utf-8");
|
||||
const newBuildNumber = Number(buildNumber) + 1;
|
||||
|
||||
if (newBuildNumber < 0) {
|
||||
throw new Error("Invalid Build Number");
|
||||
}
|
||||
|
||||
fs.writeFileSync(`${DIST_DIR}/BUILD`, String(newBuildNumber));
|
||||
|
||||
if (newBuildNumber > MAX_BUILDS) {
|
||||
const builds = fs.readdirSync(DIST_DIR);
|
||||
const buildDirs = builds.filter((build) => build.match(/build-\d+/));
|
||||
for (const buildDir of buildDirs) {
|
||||
const buildDirPath = path.join(DIST_DIR, buildDir);
|
||||
const buildDirStat = fs.statSync(buildDirPath);
|
||||
if (buildDirStat.isDirectory()) {
|
||||
const buildDirName = buildDir.split("-")[1];
|
||||
const buildDirNumber = Number(buildDirName);
|
||||
|
||||
if (buildDirNumber <= newBuildNumber - MAX_BUILDS) {
|
||||
fs.rmdirSync(buildDirPath, { recursive: true });
|
||||
console.log("Deleted Build Directory =>", buildDirPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.log("Build Number Parse Error =>", error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const spawnSyncOptions: SpawnSyncOptionsWithStringEncoding = {
|
||||
stdio: "inherit",
|
||||
encoding: "utf-8",
|
||||
shell: process.platform?.match(/win32/i) ? "bash.exe" : undefined,
|
||||
env: {
|
||||
...process.env,
|
||||
BUILDING_APP: "true",
|
||||
},
|
||||
};
|
||||
|
||||
const build = spawnSync("bunx", ["next", "build"], spawnSyncOptions);
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
function grabNewDistDir(): string {
|
||||
try {
|
||||
const buildNumber = fs.readFileSync(`${DIST_DIR}/BUILD`, "utf-8");
|
||||
return `.dist/build-${buildNumber}`;
|
||||
} catch (/** @type {*} */ error: any) {
|
||||
console.log("Build Number Parse Error =>", error.message);
|
||||
process.exit();
|
||||
}
|
||||
}
|
||||
|
||||
const newDistDir = grabNewDistDir();
|
||||
|
||||
// if (!fs.existsSync(newDistDir)) {
|
||||
// fs.mkdirSync(newDistDir, { recursive: true });
|
||||
// }
|
||||
|
||||
// if (!fs.existsSync(`${newDistDir}/BUILD_ID`)) {
|
||||
// fs.writeFileSync(`${newDistDir}/BUILD_ID`, "4");
|
||||
// }
|
||||
|
||||
/**
|
||||
* # Revert Directories
|
||||
* @param {string} dir - New Build Directory Path
|
||||
*/
|
||||
function revert(dir: string) {
|
||||
console.log("Build Failed!", build?.error?.message || build.stderr);
|
||||
fs.writeFileSync(`${DIST_DIR}/BUILD`, PREV_BUILD_NO, "utf-8");
|
||||
execSync(`rm -Rf ${dir}`, { cwd: process.cwd() });
|
||||
|
||||
const writeErr = build.error
|
||||
? build.error.message
|
||||
: build.stderr
|
||||
? build.stderr.toString()
|
||||
: "NO BUILD_ID found in New Build Folder";
|
||||
fs.writeFileSync(
|
||||
`${DIST_DIR}/LAST_BUILD_FAIL`,
|
||||
Date() + "\n\n" + writeErr,
|
||||
"utf-8"
|
||||
);
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (
|
||||
build.error ||
|
||||
build.stderr ||
|
||||
build.status != 0 ||
|
||||
!fs.existsSync(`${newDistDir}/BUILD_ID`)
|
||||
) {
|
||||
revert(newDistDir);
|
||||
throw new Error("Build Failed!");
|
||||
}
|
||||
|
||||
process.on("exit", () => {
|
||||
const onExitDir = grabNewDistDir();
|
||||
if (!fs.existsSync(`${onExitDir}/BUILD_ID`)) {
|
||||
revert(onExitDir);
|
||||
}
|
||||
});
|
@ -17,14 +17,16 @@ export default async function killChild(
|
||||
const childProcessPID = childProcess.pid;
|
||||
childProcess.kill();
|
||||
|
||||
if (typeof port == "object" && port?.[0]) {
|
||||
for (let i = 0; i < port.length; i++) {
|
||||
const singlePort = port[i];
|
||||
await kill(Number(singlePort));
|
||||
try {
|
||||
if (typeof port == "object" && port?.[0]) {
|
||||
for (let i = 0; i < port.length; i++) {
|
||||
const singlePort = port[i];
|
||||
await kill(Number(singlePort));
|
||||
}
|
||||
} else if (port) {
|
||||
await kill(Number(port));
|
||||
}
|
||||
} else if (port) {
|
||||
await kill(Number(port));
|
||||
}
|
||||
} catch (error) {}
|
||||
|
||||
return true;
|
||||
} catch (error: any) {
|
||||
|
@ -29,7 +29,7 @@ export default function startProcess({
|
||||
first_run,
|
||||
}: {
|
||||
command: string;
|
||||
preflight: string[] | string;
|
||||
preflight?: string[] | string;
|
||||
postflight?: string[] | string;
|
||||
redeploy_file: string;
|
||||
port?: string | number | (string | number)[];
|
||||
@ -38,14 +38,7 @@ export default function startProcess({
|
||||
try {
|
||||
if (first_run) {
|
||||
console.log("First Run ...");
|
||||
const runPreflight = preflightFn(preflight);
|
||||
}
|
||||
|
||||
if (!preflight) {
|
||||
console.log(
|
||||
`${colors.FgRed}Error:${colors.Reset} No preflight included in config file. If you don't want to run any preflight command simply add an empty array.`
|
||||
);
|
||||
process.exit();
|
||||
const runFirstPreflight = preflightFn(preflight);
|
||||
}
|
||||
|
||||
childProcess = run(command);
|
||||
@ -77,8 +70,6 @@ export default function startProcess({
|
||||
const runPreflight = preflightFn(preflight);
|
||||
|
||||
if (!runPreflight) {
|
||||
// TODO: Action to take if preflight fails
|
||||
|
||||
console.log(
|
||||
`${colors.FgRed}Error:${colors.Reset} Preflight Failed.`
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user