Compare commits
1 Commits
e8e57ae4b0
...
c4547d2e52
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c4547d2e52 |
@@ -12,22 +12,15 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Deploy to production
|
||||
# SSH into the host and redeploy via docker compose.
|
||||
# Set DEPLOY_HOST, DEPLOY_USER, and DEPLOY_SSH_KEY in repo secrets.
|
||||
env:
|
||||
SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
|
||||
HOST: ${{ secrets.DEPLOY_HOST }}
|
||||
USER: ${{ secrets.DEPLOY_USER }}
|
||||
REPO_DIR: ${{ secrets.DEPLOY_REPO_DIR || '/opt/nexus' }}
|
||||
run: |
|
||||
if [ -z "$SSH_KEY" ] || [ -z "$HOST" ] || [ -z "$USER" ]; then
|
||||
echo "Deploy secrets not configured — skipping remote deploy."
|
||||
echo "Set DEPLOY_HOST, DEPLOY_USER, DEPLOY_SSH_KEY in repo settings."
|
||||
exit 0
|
||||
fi
|
||||
echo "$SSH_KEY" > /tmp/deploy_key
|
||||
chmod 600 /tmp/deploy_key
|
||||
ssh -o StrictHostKeyChecking=no -i /tmp/deploy_key "$USER@$HOST" \
|
||||
"cd $REPO_DIR && git pull origin main && docker compose up -d --build nexus"
|
||||
rm /tmp/deploy_key
|
||||
- name: Deploy to host via SSH
|
||||
uses: appleboy/ssh-action@v1.0.3
|
||||
with:
|
||||
host: ${{ secrets.DEPLOY_HOST }}
|
||||
username: ${{ secrets.DEPLOY_USER }}
|
||||
key: ${{ secrets.DEPLOY_SSH_KEY }}
|
||||
script: |
|
||||
cd ~/the-nexus || git clone http://143.198.27.163:3000/Timmy_Foundation/the-nexus.git ~/the-nexus
|
||||
cd ~/the-nexus
|
||||
git fetch origin main
|
||||
git reset --hard origin/main
|
||||
./deploy.sh main
|
||||
|
||||
12
Dockerfile
12
Dockerfile
@@ -1,6 +1,6 @@
|
||||
FROM node:20-alpine
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
RUN npm install -g serve
|
||||
EXPOSE 3000
|
||||
CMD ["serve", ".", "-l", "3000", "--no-clipboard"]
|
||||
FROM nginx:alpine
|
||||
COPY . /usr/share/nginx/html
|
||||
RUN rm -f /usr/share/nginx/html/Dockerfile \
|
||||
/usr/share/nginx/html/docker-compose.yml \
|
||||
/usr/share/nginx/html/deploy.sh
|
||||
EXPOSE 80
|
||||
|
||||
27
deploy.sh
27
deploy.sh
@@ -1,20 +1,17 @@
|
||||
#!/usr/bin/env bash
|
||||
# ◈ Nexus — quick deploy helper
|
||||
# Usage:
|
||||
# ./deploy.sh # deploy main to production (port 4200)
|
||||
# ./deploy.sh staging # deploy current branch to staging (port 4201)
|
||||
|
||||
# deploy.sh — spin up (or update) the Nexus staging environment
|
||||
# Usage: ./deploy.sh — rebuild and restart nexus-main (port 4200)
|
||||
# ./deploy.sh staging — rebuild and restart nexus-staging (port 4201)
|
||||
set -euo pipefail
|
||||
|
||||
BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||
MODE=${1:-production}
|
||||
SERVICE="${1:-nexus-main}"
|
||||
|
||||
echo "◈ Nexus deploy — branch: $BRANCH mode: $MODE"
|
||||
case "$SERVICE" in
|
||||
staging) SERVICE="nexus-staging" ;;
|
||||
main) SERVICE="nexus-main" ;;
|
||||
esac
|
||||
|
||||
if [ "$MODE" = "staging" ]; then
|
||||
docker compose --profile staging up -d --build nexus-staging
|
||||
echo "✓ Staging live at http://localhost:4201 (branch: $BRANCH)"
|
||||
else
|
||||
docker compose up -d --build nexus
|
||||
echo "✓ Production live at http://localhost:4200"
|
||||
fi
|
||||
echo "==> Deploying $SERVICE …"
|
||||
docker compose build "$SERVICE"
|
||||
docker compose up -d --force-recreate "$SERVICE"
|
||||
echo "==> Done. Container: $SERVICE"
|
||||
|
||||
@@ -1,36 +1,20 @@
|
||||
version: '3.9'
|
||||
|
||||
# ◈ The Nexus — staging deployments
|
||||
#
|
||||
# Production (main):
|
||||
# docker compose up -d nexus
|
||||
# → http://<host>:4200
|
||||
#
|
||||
# Branch staging:
|
||||
# BRANCH=my-feature docker compose up -d nexus-staging
|
||||
# → http://<host>:4201
|
||||
#
|
||||
# To update production after a git pull:
|
||||
# docker compose up -d --build nexus
|
||||
version: "3.9"
|
||||
|
||||
services:
|
||||
nexus:
|
||||
nexus-main:
|
||||
build: .
|
||||
container_name: nexus-main
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "4200:3000"
|
||||
- "4200:80"
|
||||
labels:
|
||||
- "nexus.branch=main"
|
||||
- "deployment=main"
|
||||
|
||||
nexus-staging:
|
||||
build:
|
||||
context: .
|
||||
build: .
|
||||
container_name: nexus-staging
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "4201:3000"
|
||||
- "4201:80"
|
||||
labels:
|
||||
- "nexus.branch=staging"
|
||||
profiles:
|
||||
- staging
|
||||
- "deployment=staging"
|
||||
|
||||
73
index.html
73
index.html
@@ -119,50 +119,53 @@
|
||||
|
||||
<script type="module" src="./app.js"></script>
|
||||
|
||||
<!-- Live Reload: polls Gitea for new commits, refreshes when main advances -->
|
||||
<div id="update-banner" style="display:none;position:fixed;top:0;left:0;right:0;z-index:9999;
|
||||
background:linear-gradient(90deg,#4af0c0,#7b5cff);color:#050510;
|
||||
font-family:'JetBrains Mono',monospace;font-size:13px;font-weight:600;
|
||||
text-align:center;padding:8px;cursor:pointer;letter-spacing:0.05em;"
|
||||
onclick="location.reload()">
|
||||
◈ NEW VERSION DEPLOYED — click to reload
|
||||
</div>
|
||||
<!-- Live Refresh: polls Gitea for new commits on main, reloads when SHA changes -->
|
||||
<div id="live-refresh-banner" style="
|
||||
display:none; position:fixed; top:0; left:0; right:0; z-index:9999;
|
||||
background:linear-gradient(90deg,#4af0c0,#7b5cff);
|
||||
color:#050510; font-family:'JetBrains Mono',monospace; font-size:13px;
|
||||
padding:8px 16px; text-align:center; font-weight:600;
|
||||
">⚡ NEW DEPLOYMENT DETECTED — Reloading in <span id="lr-countdown">5</span>s…</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
const GITEA_API = 'http://143.198.27.163:3000/api/v1';
|
||||
(function() {
|
||||
const GITEA = 'http://143.198.27.163:3000/api/v1';
|
||||
const REPO = 'Timmy_Foundation/the-nexus';
|
||||
const BRANCH = 'main';
|
||||
const INTERVAL = 30000; // 30s
|
||||
const INTERVAL = 30000; // poll every 30s
|
||||
|
||||
let knownSha = null;
|
||||
|
||||
async function checkForUpdates() {
|
||||
async function fetchLatestSha() {
|
||||
try {
|
||||
const res = await fetch(
|
||||
`${GITEA_API}/repos/${REPO}/commits?sha=${BRANCH}&limit=1`,
|
||||
{ cache: 'no-store' }
|
||||
);
|
||||
if (!res.ok) return;
|
||||
const commits = await res.json();
|
||||
if (!commits || !commits[0]) return;
|
||||
const sha = commits[0].sha;
|
||||
if (knownSha === null) {
|
||||
knownSha = sha;
|
||||
return;
|
||||
}
|
||||
if (sha !== knownSha) {
|
||||
document.getElementById('update-banner').style.display = 'block';
|
||||
// Auto-reload after 5s
|
||||
setTimeout(() => location.reload(), 5000);
|
||||
}
|
||||
} catch (_) { /* offline or network error — skip */ }
|
||||
const r = await fetch(`${GITEA}/repos/${REPO}/branches/${BRANCH}`, { cache: 'no-store' });
|
||||
if (!r.ok) return null;
|
||||
const d = await r.json();
|
||||
return d.commit && d.commit.id ? d.commit.id : null;
|
||||
} catch (e) { return null; }
|
||||
}
|
||||
|
||||
// Start polling once page is loaded
|
||||
window.addEventListener('load', () => {
|
||||
checkForUpdates();
|
||||
setInterval(checkForUpdates, INTERVAL);
|
||||
});
|
||||
async function poll() {
|
||||
const sha = await fetchLatestSha();
|
||||
if (!sha) return;
|
||||
if (knownSha === null) { knownSha = sha; return; }
|
||||
if (sha !== knownSha) {
|
||||
knownSha = sha;
|
||||
const banner = document.getElementById('live-refresh-banner');
|
||||
const countdown = document.getElementById('lr-countdown');
|
||||
banner.style.display = 'block';
|
||||
let t = 5;
|
||||
const tick = setInterval(() => {
|
||||
t--;
|
||||
countdown.textContent = t;
|
||||
if (t <= 0) { clearInterval(tick); location.reload(); }
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
// Start polling after page is interactive
|
||||
fetchLatestSha().then(sha => { knownSha = sha; });
|
||||
setInterval(poll, INTERVAL);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user