Updated deployment process

This commit is contained in:
Prozilla 2024-06-10 17:15:34 +02:00
parent ba166fc7f0
commit 0eeb87cd7b
No known key found for this signature in database
GPG key ID: 5858DFE71CAF31EE
8 changed files with 150 additions and 137 deletions

View file

@ -1,42 +1,22 @@
#!/bin/bash
# Edit these variables before deploying
DOMAIN="os.prozilla.dev"
COMMIT_MESSAGE="Deployed build to GitHub Pages"
REPO_URL="https://github.com/Prozilla/ProzillaOS"
# ------- You don't need to edit anything below this line -------
echo -e "Domain: \e[0;36m$DOMAIN\e[0m"
echo -e "\e[0;33mDeploying to GitHub Pages...\e[0m"
domain=$(cat dist/CNAME)
echo -e "Domain: \e[0;36m$domain\e[0m"
echo -e "Commit message: \e[0;36m$COMMIT_MESSAGE\e[0m"
echo -e "Repository: \e[0;36m$REPO_URL\e[0m\n"
echo -e "\e[0;33mConfiguring routing...\e[0m"
echo $DOMAIN > dist/CNAME
cp dist/index.html dist/404.html
# Use template and pages data to generate index.html files
input="tmp/pages.csv"
template="tmp/template.html"
while IFS=";" read -r app_id app_name app_description
do
path="dist/$app_id/index.html"
echo -e "- dist/\e[0;36m$app_id/index.html\e[0m"
mkdir -p "dist/$app_id"
cp $template $path
# Fill template
sed -i "s/_APP_NAME/$app_name/g" $path
sed -i "s/_APP_DESCRIPTION/$app_description/g" $path
sed -i "s/_APP_ID/$app_id/g" $path
done < <(tail -n +2 $input)
rm -r tmp
echo -e "Repository: \e[0;36m$REPO_URL\e[0m"
echo ""
echo -e "\e[0;33mDeploying to GitHub Pages...\e[0m"
if gh-pages -d dist -m $COMMIT_MESSAGE -r $REPO_URL ; then
echo -e "\e[0;32m✓ Successfully deployed to \e[0;36mhttps://$DOMAIN/\e[0m"
if gh-pages -x -d dist -m $COMMIT_MESSAGE -r $REPO_URL ; then
echo -e "\e[0;32m✓ Successfully deployed to \e[0;36mhttps://$domain/\e[0m"
else
echo -e "\e[0;31mFailed to deploy\e[0m"
fi

View file

@ -18,7 +18,7 @@
<meta name="apple-mobile-web-app-title" content="ProzillaOS">
<meta name="application-name" content="ProzillaOS">
<meta name="msapplication-TileColor" content="#0d1114">
<meta name="theme-color" content="#4DFFE1">
<meta name="theme-color" content="#4D9CFF">
<!-- Open Graph -->
<meta property="og:title" content="ProzillaOS | Web-based Operating System"/>
@ -33,7 +33,7 @@
<meta name="twitter:title" content="ProzillaOS | Web-based Operating System"/>
<meta name="twitter:description" content="ProzillaOS is an open-source web-based operating system inspired by Ubuntu Linux and Windows made with React.js by Prozilla."/>
<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:image" content="/assets/banner-logo-title.png?v=2"/>
<meta name="twitter:image" content="https://os.prozilla.dev/assets/banner-logo-title.png?v=2"/>
<meta name="twitter:url" content="https://os.prozilla.dev/">
<!-- Fonts -->
@ -53,7 +53,7 @@
"mainEntity": [
{
"@type": "Question",
"name": "Is ProzillaOS open source?",
"name": "Is ProzillaOS open-source?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Yes, the source code for ProzillaOS is hosted on <a href=\"https://github.com/Prozilla/ProzillaOS\">GitHub</a>."
@ -70,6 +70,8 @@
]
}
</script>
<!-- Inserted tags -->
</head>
<body>
<noscript>You need to enable JavaScript to run ProzillaOS.</noscript>

View file

@ -9,10 +9,10 @@
"scripts": {
"start": "vite --port 3000 --host",
"build": "tsc && vite build",
"serve": "vite preview",
"predeploy": "npm run build && npm run prep",
"deploy": "sh deploy.sh",
"prep": "node --no-warnings --loader ts-node/esm ./src/tools/prep"
"serve": "vite preview --port 8080 --host",
"predeploy": "npm run build && npm run stage",
"stage": "node --no-warnings --loader ts-node/esm ./src/tools/stage",
"deploy": "sh deploy.sh"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.4.0",

View file

@ -4,9 +4,9 @@
This is ProzillaOS, a web-based operating system made with React.js by [Prozilla](https://prozilla.dev/).
## Open Source
## Open-source
ProzillaOS is [open source](https://github.com/Prozilla/ProzillaOS)! Feel free to fork this project and create your own OS or share feedback by creating an issue on the GitHub page.
ProzillaOS is [open-source](https://github.com/Prozilla/ProzillaOS)! Feel free to fork this project and create your own OS or share feedback by creating an issue on the GitHub page.
## Support ProzillaOS

View file

@ -1,4 +0,0 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:
Sitemap: https://os.prozilla.dev/sitemap.xml

View file

@ -2,7 +2,8 @@ import { ANSI } from "./apps/terminal.config";
export const NAME = "ProzillaOS";
export const TAG_LINE = "Web-based Operating System";
export const BASE_URL = "https://os.prozilla.dev/";
export const DOMAIN = "os.prozilla.dev";
export const BASE_URL = `https://${DOMAIN}/`;
export const ASCII_LOGO = `
:.

View file

@ -1,95 +0,0 @@
import fs from "node:fs";
import { APP_DESCRIPTIONS, APP_NAMES, APPS } from "../config/apps.config";
import { ANSI } from "../config/apps/terminal.config";
import { BASE_URL, NAME, TAG_LINE } from "../config/branding.config";
import { WALLPAPERS } from "../config/desktop.config";
const PATH_TO_SITEMAP = "dist/sitemap.xml";
const PATH_TO_PAGES = "tmp/pages.csv";
const PATH_TO_INDEX = "dist/index.html";
const PATH_TO_TEMPLATE = "tmp/template.html";
function generateSitemap() {
const date = new Date();
const lastModified = `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;
const images = WALLPAPERS.map((path) => `
<image:image>
<image:loc>${BASE_URL.slice(0, -1) + path}</image:loc>
</image:image>`
);
const pages = Object.values(APPS).map((appId) => `
<url>
<loc>${BASE_URL + appId}</loc>
<lastmod>${lastModified}</lastmod>
</url>`
);
const sitemap = `
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xml>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">
<url>
<loc>${BASE_URL}</loc>
<lastmod>${lastModified}</lastmod>
${images.join("")}
</url>
${pages.join("")}
</urlset>`;
return sitemap.trim();
}
function generatePageRecords() {
const records = ["id;name;description"];
for (const [key, value] of Object.entries(APPS)) {
const id = value;
const name = (Object.keys(APP_NAMES).includes(key) ? APP_NAMES[key] : id) as string;
const description = (Object.keys(APP_DESCRIPTIONS).includes(key) ? APP_DESCRIPTIONS[key] : TAG_LINE) as string;
records.push(`${id};${name};${description}`);
}
return records.join("\n") + "\n";
}
function generatePageTemplate() {
let html = fs.readFileSync(PATH_TO_INDEX, "utf-8");
const titleRegex = /(?<=(<title>|<meta property="og:title" content="|<meta name="twitter:title" content="))(([a-zA-Z|-]|\s)+)(?=(<\/title>|"\/?>))/g;
html = html.replaceAll(titleRegex, `_APP_NAME | ${NAME}`);
const descriptionRegex = /(?<=(<meta name="description" content="|<meta property="og:description" content="|<meta name="twitter:description" content="))(([a-zA-Z-.]|\s)+)(?=("\/?>))/g;
html = html.replaceAll(descriptionRegex, "_APP_DESCRIPTION");
const canonicalRegex = /(?<=(<link rel="canonical" href="|<meta name="twitter:url" content="|<meta property="og:url" content="))(http(s)?:\/\/[a-zA-Z-.]+\/)(?=("\/?>))/g;
html = html.replaceAll(canonicalRegex, `${BASE_URL}_APP_ID`);
const faqRegex = /<!-- FAQ -->.*?<script type="application\/ld\+json">.*?<\/script>/gs;
html = html.replaceAll(faqRegex, "");
return html;
}
try {
const steps: [string, () => string][] = [
[PATH_TO_SITEMAP, generateSitemap],
[PATH_TO_TEMPLATE, generatePageTemplate],
[PATH_TO_PAGES, generatePageRecords],
];
steps.forEach(([path, generateContent]) => {
const directory = path.substring(0, path.lastIndexOf("/"));
if (directory != "" && !fs.existsSync(directory)){
fs.mkdirSync(directory, { recursive: true });
}
fs.writeFileSync(path, generateContent(), { flag: "w+" });
console.log(`${ANSI.fg.green}✓ Generated ${path}${ANSI.reset}`);
});
} catch (error) {
console.error(error);
}

129
src/tools/stage.ts Normal file
View file

@ -0,0 +1,129 @@
import fs from "node:fs";
import { APP_DESCRIPTIONS, APP_NAMES, APPS } from "../config/apps.config";
import { ANSI } from "../config/apps/terminal.config";
import { BASE_URL, DOMAIN, NAME, TAG_LINE } from "../config/branding.config";
import { WALLPAPERS } from "../config/desktop.config";
const BUILD_DIR = "dist";
const PATHS = {
sitemapXml: BUILD_DIR + "/sitemap.xml",
robotsTxt: BUILD_DIR + "/robots.txt",
indexHtml: BUILD_DIR + "/index.html",
cname: BUILD_DIR + "/CNAME",
};
function generateSitemapXml() {
const date = new Date();
const lastModified = `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;
const images = WALLPAPERS.map((path) => `
<image:image>
<image:loc>${BASE_URL.slice(0, -1) + path}</image:loc>
</image:image>`
);
const pages = Object.values(APPS).map((appId) => `
<url>
<loc>${BASE_URL + appId}</loc>
<lastmod>${lastModified}</lastmod>
</url>`
);
const sitemap = `
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xml>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">
<url>
<loc>${BASE_URL}</loc>
<lastmod>${lastModified}</lastmod>
${images.join("")}
</url>
${pages.join("")}
</urlset>`;
return sitemap.trim();
}
function generateRobotsTxt() {
const sitemapUrl = BASE_URL + PATHS.sitemapXml.replace(BUILD_DIR + "/", "");
return `# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:
Sitemap: ${sitemapUrl}`;
}
function generateCname() {
return DOMAIN;
}
/**
* To avoid GitHub pages rendering certain pages that are only defined by React router as a 404 page,
* we copy the content of our index file to the 404 page, letting React router properly handle routing on every page
*/
function generate404Page(template: string) {
const path = `${BUILD_DIR}/404.html`;
fs.writeFileSync(path, template, { flag: "w+" });
console.log(`- ${ANSI.fg.cyan}${path}${ANSI.reset}`);
}
function generateAppPages(template: string) {
for (const [key, value] of Object.entries(APPS)) {
const appId = value;
const appName = Object.keys(APP_NAMES).includes(key) ? APP_NAMES[key] as string : appId;
const appDescription = Object.keys(APP_DESCRIPTIONS).includes(key) ? APP_DESCRIPTIONS[key] as string : TAG_LINE;
if (appId === "index") {
console.log("Invalid app ID found: " + appId);
return;
}
let html = template;
const titleRegex = /(?<=(<title>|<meta property="og:title" content="|<meta name="twitter:title" content="))(([a-zA-Z|-]|\s)+)(?=(<\/title>|"\/?>))/g;
html = html.replaceAll(titleRegex, `${appName} | ${NAME}`);
const descriptionRegex = /(?<=(<meta name="description" content="|<meta property="og:description" content="|<meta name="twitter:description" content="))(([a-zA-Z-.]|\s)+)(?=("\/?>))/g;
html = html.replaceAll(descriptionRegex, appDescription);
const canonicalRegex = /(?<=(<link rel="canonical" href="|<meta name="twitter:url" content="|<meta property="og:url" content="))(http(s)?:\/\/[a-zA-Z-.]+\/)(?=("\/?>))/g;
html = html.replaceAll(canonicalRegex, BASE_URL + appId);
const faqRegex = /<!-- FAQ -->.*?<script type="application\/ld\+json">.*?<\/script>/gs;
html = html.replaceAll(faqRegex, "");
const path = `${BUILD_DIR}/${appId}.html`;
fs.writeFileSync(path, html, { flag: "w+" });
console.log(`- ${ANSI.fg.cyan}${path}${ANSI.reset}`);
}
}
try {
console.log(`${ANSI.fg.yellow}Staging build...${ANSI.reset}`);
const files: [string, () => string][] = [
[PATHS.sitemapXml, generateSitemapXml],
[PATHS.robotsTxt, generateRobotsTxt],
[PATHS.cname, generateCname],
];
files.forEach(([path, generateContent]) => {
const directory = path.substring(0, path.lastIndexOf("/"));
if (directory != "" && !fs.existsSync(directory)){
fs.mkdirSync(directory, { recursive: true });
}
fs.writeFileSync(path, generateContent(), { flag: "w+" });
console.log(`- ${ANSI.fg.cyan}${path}${ANSI.reset}`);
});
console.log(`\n${ANSI.fg.yellow}Generating pages...${ANSI.reset}`);
const template = fs.readFileSync(PATHS.indexHtml, "utf-8");
generate404Page(template);
generateAppPages(template);
console.log(`\n${ANSI.fg.green}✓ Staging complete${ANSI.reset}`);
} catch (error) {
console.error(error);
}