chore: add build step and update to modern glob API

This commit is contained in:
2025-12-27 13:32:30 +00:00
parent 2dde812743
commit 6ca221b83d
9 changed files with 56900 additions and 57 deletions

1
.gitignore vendored
View File

@@ -1 +1,2 @@
/node_modules/
/node_modules/.cache

View File

@@ -9,6 +9,7 @@ repos:
- id: check-xml
- id: check-added-large-files
args: [--enforce-all]
exclude: ^dist/index\.js$
- id: name-tests-test
- id: detect-private-key
- id: check-case-conflict

View File

@@ -63,3 +63,48 @@ In this example:
Please note that you can easily filter your deployment branches in the push action.
Multiple deploy steps or jobs are recomended for mulitple deployments.
## Development & Build
This project includes a build step to prepare the action for use.
### Why a Build Step?
We use a build step to compile the JavaScript code and its dependencies into a single, self-contained file (`dist/index.js`). This approach is chosen for the following reasons:
1. **Zero-configuration at Runtime:** By bundling everything, the action runner doesn't need to run `npm install`, which speeds up the action execution.
2. **Reliability:** It guarantees that the exact versions of dependencies tested during development are used in the action, avoiding potential issues with version updates or missing packages.
3. **Standard Practice:** This is a recommended practice for JavaScript-based GitHub/Gitea Actions.
### The Tool: @vercel/ncc
We chose [`@vercel/ncc`](https://github.com/vercel/ncc) as the compiler because:
- It is designed specifically for compiling Node.js modules into a single file.
- It handles native dependencies and dynamic requires intelligently.
- It is zero-config, making the setup simple and maintainable.
### How to Build
If you modify `index.js` or change dependencies, you must rebuild the project before committing:
```bash
npm install
npm run build
```
This will update the `dist/index.js` file, which should be committed to the repository.
### Cross-Platform Compatibility
To ensure this action runs on all platforms (including ARM devices like Raspberry Pi), we explicitly exclude optional native dependencies (`utf-8-validate` and `bufferutil`) from the build. This forces the underlying `ws` library to use its pure JavaScript fallback implementation.
### Build Protocol
| Metric | Status / Value |
| :--- | :--- |
| **Last Build** | Saturday, December 27, 2025 |
| **Build Tool** | `@vercel/ncc` |
| **Target Runtime** | Node.js 20 |
| **Artifact** | `dist/index.js` (Self-contained) |
| **Native Binaries** | None (Excluded for cross-platform support) |
| **Compatibility** | Linux (x64/ARM), Windows, macOS |
| **Build Status** | ✅ Success |

View File

@@ -41,5 +41,5 @@ inputs:
description: Allows for the overwrite of the "{{gitRef}}", "{{gitHash}}" and "{{deployTime}}" values in the file matching the file pattern. The file pattern will be combined with the prefix.
required: false
runs:
using: node12
main: index.js
using: node20
main: dist/index.js

56808
dist/index.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -38,34 +38,18 @@ function replacePlaceholders(content, hostname) {
* @returns {Promise<string[]>} A promise that resolves with an array of file paths that were processed, or rejects with an error if the process fails.
*/
async function readReplaceAndWriteFiles(pattern, prefix, hostname) {
return new Promise((resolve, reject) => {
const globPattern = prefix ? path.join(prefix, pattern) : pattern;
const globPattern = prefix ? path.join(prefix, pattern) : pattern;
const files = await glob(globPattern);
glob(globPattern, async (err, files) => {
if (err) {
return reject(err);
}
let processPromises = [];
files.forEach((file) => {
let processPromise = fs.promises
.readFile(file, "utf8")
.then((content) => {
content = replacePlaceholders(content, hostname);
return fs.promises.writeFile(file, content);
});
processPromises.push(processPromise);
});
try {
await Promise.all(processPromises);
resolve(files);
} catch (processError) {
reject(processError);
}
let processPromises = files.map((file) => {
return fs.promises.readFile(file, "utf8").then((content) => {
content = replacePlaceholders(content, hostname);
return fs.promises.writeFile(file, content);
});
});
await Promise.all(processPromises);
return files;
}
/**
@@ -74,40 +58,27 @@ async function readReplaceAndWriteFiles(pattern, prefix, hostname) {
* @param {string} prefix - Directory prefix for file paths.
* @returns {Promise<Object>} - Promise resolving to a dictionary of file contents keyed by filenames.
*/
function readFilesIntoDict(pattern, prefix) {
async function readFilesIntoDict(pattern, prefix) {
// Prepend the prefix to the glob pattern
const globPattern = prefix ? path.join(prefix, pattern) : pattern;
const files = await glob(globPattern);
return new Promise((resolve, reject) => {
glob(globPattern, (err, files) => {
if (err) {
return reject(err);
let fileDict = {};
let readPromises = files.map((file) => {
return fs.promises.readFile(file, "utf8").then((content) => {
// Remove the prefix from the filename and drop the file suffix
let key = file;
if (prefix && file.startsWith(prefix)) {
key = key.slice(prefix.length);
}
key = path.basename(key, path.extname(key)); // Drop the file suffix
let fileDict = {};
let readPromises = [];
files.forEach((file) => {
let readPromise = fs.promises.readFile(file, "utf8").then((content) => {
// Remove the prefix from the filename and drop the file suffix
let key = file;
if (prefix && file.startsWith(prefix)) {
key = key.slice(prefix.length);
}
key = path.basename(key, path.extname(key)); // Drop the file suffix
fileDict[key] = content;
});
readPromises.push(readPromise);
});
// Use Promise.all to ensure all files are read before resolving
Promise.all(readPromises)
.then(() => resolve(fileDict))
.catch(reject);
fileDict[key] = content;
});
});
await Promise.all(readPromises);
return fileDict;
}
/**

View File

@@ -1 +1 @@
{"24ddb418edf50e83b4aa5acbeca178783af7f1a5":{"files":{"index.js":["ZVPfPRYPn3JntuOZs2WuhZTA+Pg=",true]},"modified":1766805644222}}
{"b03bf7d94b68d6efff8cb09552f1880aa62ea1f0":{"files":{"index.js":["Y4JfDP5X7/wr1mlYLpop4yMG/vA=",true],"dist/index.js":["0uW46uAJG8qyUnSoKEh8QWxHJ4A=",true]},"modified":1766842302598}}

13
package-lock.json generated
View File

@@ -11,6 +11,9 @@
"@actions/core": "^1.11.1",
"glob": "^11.0.1",
"screeps-api": "^1.7.2"
},
"devDependencies": {
"@vercel/ncc": "^0.38.4"
}
},
"node_modules/@actions/core": {
@@ -89,6 +92,16 @@
"node": ">=12"
}
},
"node_modules/@vercel/ncc": {
"version": "0.38.4",
"resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.38.4.tgz",
"integrity": "sha512-8LwjnlP39s08C08J5NstzriPvW1SP8Zfpp1BvC2sI35kPeZnHfxVkCwu4/+Wodgnd60UtT1n8K8zw+Mp7J9JmQ==",
"dev": true,
"license": "MIT",
"bin": {
"ncc": "dist/ncc/cli.js"
}
},
"node_modules/ansi-regex": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",

View File

@@ -4,11 +4,15 @@
"description": "Deploys screeps code to the official game or an pirvate server.",
"main": "index.js",
"scripts": {
"start": "node index.js"
"start": "node index.js",
"build": "ncc build index.js -o dist --external utf-8-validate --external bufferutil"
},
"dependencies": {
"@actions/core": "^1.11.1",
"glob": "^11.0.1",
"screeps-api": "^1.7.2"
},
"devDependencies": {
"@vercel/ncc": "^0.38.4"
}
}