Skip to content

Build & Tag (CDN Ready) #14

Build & Tag (CDN Ready)

Build & Tag (CDN Ready) #14

Workflow file for this run

# ccmjs workflow v3.0.0
name: Build & Tag (CDN Ready)
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
on:
workflow_dispatch:
inputs:
version:
description: "Version (e.g. 1.0.0)"
required: true
permissions:
contents: write
jobs:
tag:
runs-on: ubuntu-latest
steps:
- name: Checkout main
uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 22
- name: Install dependencies
run: npm install terser clean-css
- name: Build files
run: |
node <<'EOF'
import fs from "fs";
import path from "path";
import { minify } from "terser";
import CleanCSS from "clean-css";
const VERSION = process.env.VERSION;
const repo = process.env.GITHUB_REPOSITORY;
const [owner, name] = repo.split("/");
const BASE_URL = `https://${owner}.github.io/${name}/`;
const CDN_BASE = `https://cdn.jsdelivr.net/gh/${owner}/${name}@v${VERSION}/`;
// ---------- find main file ----------
const rootFiles = fs.readdirSync(".");
const mainFile = rootFiles.find(f => f.endsWith(".mjs"));
if (!mainFile) throw new Error("No main .mjs file found");
// ---------- collect resources ----------
function collect(dir) {
if (!fs.existsSync(dir)) return [];
const result = [];
function walk(current) {
const entries = fs.readdirSync(current, { withFileTypes: true });
for (const entry of entries) {
const full = path.join(current, entry.name);
if (entry.isDirectory()) {
walk(full);
} else {
result.push(full);
}
}
}
walk(dir);
return result;
}
const resourceFiles = collect("resources");
// ---------- JS / MJS ----------
async function processJS(file, isMain = false) {
let content = fs.readFileSync(file, "utf-8");
content = content.replaceAll("././", BASE_URL);
let outputName = file;
if (isMain) {
outputName = file.replace(".mjs", `-${VERSION}.mjs`);
}
const mapURL = CDN_BASE + outputName + ".map";
const result = await minify(content, {
compress: true,
mangle: true,
sourceMap: {
filename: outputName,
url: false
}
});
const map = JSON.parse(result.map);
map.file = outputName;
map.sourceRoot = "ccmjs:///";
map.sources = [file];
map.sourcesContent = [content];
const minName = outputName.replace(/\.(m?js)$/, ".min.$1");
let code = result.code.replace(/\/\/# sourceMappingURL=.*$/gm, "");
fs.mkdirSync(path.dirname(minName), { recursive: true });
fs.writeFileSync(
minName,
code + `\n//# sourceMappingURL=${mapURL}`
);
fs.writeFileSync(
outputName + ".map",
JSON.stringify(map)
);
}
// ---------- CSS ----------
function processCSS(file) {
const content = fs.readFileSync(file, "utf-8");
const minified = new CleanCSS().minify(content).styles;
const minName = file.replace(".css", ".min.css");
fs.mkdirSync(path.dirname(minName), { recursive: true });
fs.writeFileSync(minName, minified);
}
// ---------- RUN ----------
await processJS(mainFile, true);
for (const file of resourceFiles) {
if (file.endsWith(".js") || file.endsWith(".mjs")) {
await processJS(file);
}
else if (file.endsWith(".css")) {
processCSS(file);
}
// images etc. bleiben unverändert
}
// ---------- CLEAN ROOT ----------
for (const file of fs.readdirSync(".")) {
if (file === ".git") continue;
// keep output
if (file.includes(".min.")) continue;
if (file.endsWith(".map")) continue;
// keep folders
if (file === "resources") continue;
if (file === "libs") continue;
// delete original main file
if (file === mainFile) {
fs.rmSync(file, { force: true });
continue;
}
// delete everything else
fs.rmSync(file, { recursive: true, force: true });
}
// ---------- CLEAN ORIGINALS IN RESOURCES ----------
function cleanResources(dir = "resources") {
if (!fs.existsSync(dir)) return;
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const full = path.join(dir, entry.name);
if (entry.isDirectory()) {
cleanResources(full);
} else {
if (
full.endsWith(".js") ||
full.endsWith(".mjs") ||
full.endsWith(".css")
) {
fs.rmSync(full, { force: true });
}
}
}
}
cleanResources();
EOF
env:
VERSION: ${{ github.event.inputs.version }}
- name: Commit build
run: |
git config user.name "github-actions"
git config user.email "github-actions@github.com"
git add .
git commit -m "build v${{ github.event.inputs.version }}"
- name: Check if tag exists
run: |
if git rev-parse "v${{ github.event.inputs.version }}" >/dev/null 2>&1; then
echo "❌ Tag already exists"
exit 1
fi
- name: Create and push tag
run: |
git tag v${{ github.event.inputs.version }}
git push origin v${{ github.event.inputs.version }}