Updated deployment process
This commit is contained in:
parent
ba166fc7f0
commit
0eeb87cd7b
8 changed files with 150 additions and 137 deletions
36
deploy.sh
36
deploy.sh
|
|
@ -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
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
Sitemap: https://os.prozilla.dev/sitemap.xml
|
||||
|
|
@ -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 = `
|
||||
:.
|
||||
|
|
|
|||
|
|
@ -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
129
src/tools/stage.ts
Normal 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);
|
||||
}
|
||||
Loading…
Reference in a new issue