docs: Ledger Live integration, contract deploy learnings, NEXT_STEPS updates
Some checks failed
Deploy to Phoenix / deploy (push) Has been cancelled
Some checks failed
Deploy to Phoenix / deploy (push) Has been cancelled
- ADD_CHAIN138_TO_LEDGER_LIVE: Ledger form done; public code review repo bis-innovations/LedgerLive; init/push commands - CONTRACT_DEPLOYMENT_RUNBOOK: Chain 138 gas price 1 gwei, 36-addr check, TransactionMirror workaround - CONTRACT_*: AddressMapper, MirrorManager deployed 2026-02-12; 36-address on-chain check - NEXT_STEPS_FOR_YOU: Ledger done; steps completable now (no LAN); run-completable-tasks-from-anywhere - MASTER_INDEX, OPERATOR_OPTIONAL, SMART_CONTRACTS_INVENTORY_SIMPLE: updates - LEDGER_BLOCKCHAIN_INTEGRATION_COMPLETE: bis-innovations/LedgerLive reference Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
17
phoenix-deploy-api/.env.example
Normal file
17
phoenix-deploy-api/.env.example
Normal file
@@ -0,0 +1,17 @@
|
||||
# =============================================================================
|
||||
# Phoenix Deploy API — Environment (example)
|
||||
# =============================================================================
|
||||
# Copy to .env and fill in. Do not commit .env.
|
||||
# See README.md for Gitea webhook and deploy endpoint usage.
|
||||
# =============================================================================
|
||||
|
||||
# Listen port
|
||||
PORT=4001
|
||||
|
||||
# Gitea instance (for commit status API)
|
||||
GITEA_URL=https://gitea.d-bis.org
|
||||
# Token with repo (or repo:status) scope — create at Gitea → Settings → Applications
|
||||
GITEA_TOKEN=your-gitea-token
|
||||
|
||||
# Optional: shared secret for webhook signature and /api/deploy Bearer auth
|
||||
# PHOENIX_DEPLOY_SECRET=your-webhook-and-deploy-secret
|
||||
6
phoenix-deploy-api/.env.template
Normal file
6
phoenix-deploy-api/.env.template
Normal file
@@ -0,0 +1,6 @@
|
||||
# Copy to .env and set real values. .env is gitignored.
|
||||
|
||||
PORT=4001
|
||||
GITEA_URL=https://gitea.d-bis.org
|
||||
GITEA_TOKEN=
|
||||
# PHOENIX_DEPLOY_SECRET=
|
||||
7
phoenix-deploy-api/.gitignore
vendored
Normal file
7
phoenix-deploy-api/.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
node_modules/
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
*.log
|
||||
logs/
|
||||
.DS_Store
|
||||
40
phoenix-deploy-api/DEPLOY.md
Normal file
40
phoenix-deploy-api/DEPLOY.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Deploy Phoenix Deploy API
|
||||
|
||||
## Local run (no install)
|
||||
|
||||
From this directory:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
cp .env.example .env # then set GITEA_TOKEN
|
||||
npm start
|
||||
```
|
||||
|
||||
## Install as systemd service (production)
|
||||
|
||||
1. **From this repo** (dependencies already installed in repo):
|
||||
|
||||
```bash
|
||||
sudo ./scripts/install-systemd.sh
|
||||
```
|
||||
|
||||
Or from repo root:
|
||||
|
||||
```bash
|
||||
sudo phoenix-deploy-api/scripts/install-systemd.sh
|
||||
```
|
||||
|
||||
2. **Edit env** on the server:
|
||||
|
||||
```bash
|
||||
sudo nano /opt/phoenix-deploy-api/.env
|
||||
# Set GITEA_TOKEN=... and optionally PHOENIX_DEPLOY_SECRET
|
||||
sudo systemctl restart phoenix-deploy-api
|
||||
```
|
||||
|
||||
3. **Check**: `curl http://localhost:4001/health`
|
||||
|
||||
## If you don't have sudo
|
||||
|
||||
- Run in project dir: `npm install && npm start` (or use PM2/screen).
|
||||
- To install to `/opt` and systemd, run `install-systemd.sh` on a host where you have sudo.
|
||||
51
phoenix-deploy-api/README.md
Normal file
51
phoenix-deploy-api/README.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Phoenix Deploy API
|
||||
|
||||
Gitea webhook receiver and deploy endpoint stub for Gitea → Phoenix deployment integration.
|
||||
|
||||
## Endpoints
|
||||
|
||||
| Method | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| POST | /webhook/gitea | Receives Gitea push/tag/PR webhooks |
|
||||
| POST | /api/deploy | Deploy request (repo, branch, target) |
|
||||
| GET | /health | Health check |
|
||||
|
||||
## Environment
|
||||
|
||||
Copy `.env.example` to `.env` and set `GITEA_TOKEN` (and optionally `PHOENIX_DEPLOY_SECRET`).
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| PORT | 4001 | Listen port |
|
||||
| GITEA_URL | https://gitea.d-bis.org | Gitea instance URL |
|
||||
| GITEA_TOKEN | | Token for commit status API |
|
||||
| PHOENIX_DEPLOY_SECRET | | Optional secret for webhook/deploy auth |
|
||||
|
||||
## Gitea Webhook Configuration
|
||||
|
||||
In Gitea: Repository → Settings → Webhooks → Add Webhook
|
||||
|
||||
- **URL:** `https://phoenix-api-host/api/webhook/gitea` (or your Phoenix API URL)
|
||||
- **Content type:** application/json
|
||||
- **Events:** Push events, Tag creation (and optionally Pull requests)
|
||||
- **Secret:** Optional, set PHOENIX_DEPLOY_SECRET to match
|
||||
|
||||
## Deploy API (Trigger from Gitea Actions)
|
||||
|
||||
```bash
|
||||
curl -X POST "https://phoenix-api-host/api/deploy" \
|
||||
-H "Authorization: Bearer $PHOENIX_DEPLOY_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"repo":"d-bis/proxmox","branch":"main","sha":"abc123","target":"default"}'
|
||||
```
|
||||
|
||||
## Integration with Sankofa Phoenix
|
||||
|
||||
This service is a standalone stub. Full deployment logic should be implemented in the Sankofa Phoenix API (VMID 8600). Migrate the webhook handler and deploy logic into the Phoenix API when ready.
|
||||
|
||||
## Run
|
||||
|
||||
```bash
|
||||
npm install
|
||||
GITEA_TOKEN=xxx npm start
|
||||
```
|
||||
14
phoenix-deploy-api/package.json
Normal file
14
phoenix-deploy-api/package.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "phoenix-deploy-api",
|
||||
"version": "1.0.0",
|
||||
"description": "Phoenix deploy API stub and Gitea webhook receiver for Gitea→Phoenix deployment integration",
|
||||
"type": "module",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"start": "node server.js",
|
||||
"dev": "node --watch server.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"express": "^4.21.0"
|
||||
}
|
||||
}
|
||||
17
phoenix-deploy-api/phoenix-deploy-api.service
Normal file
17
phoenix-deploy-api/phoenix-deploy-api.service
Normal file
@@ -0,0 +1,17 @@
|
||||
[Unit]
|
||||
Description=Phoenix Deploy API - Gitea webhook receiver
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
WorkingDirectory=/opt/phoenix-deploy-api
|
||||
ExecStart=/bin/node server.js
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
Environment=PORT=4001
|
||||
Environment=GITEA_URL=https://gitea.d-bis.org
|
||||
EnvironmentFile=-/opt/phoenix-deploy-api/.env
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
29
phoenix-deploy-api/scripts/install-systemd.sh
Normal file
29
phoenix-deploy-api/scripts/install-systemd.sh
Normal file
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env bash
|
||||
# Install Phoenix Deploy API to /opt/phoenix-deploy-api and enable systemd service.
|
||||
# Run with: sudo ./scripts/install-systemd.sh
|
||||
# Or from repo root: sudo phoenix-deploy-api/scripts/install-systemd.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
APP_DIR="$SCRIPT_DIR/.."
|
||||
TARGET="/opt/phoenix-deploy-api"
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "Run with sudo to install to $TARGET and install systemd unit."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Installing Phoenix Deploy API to $TARGET ..."
|
||||
mkdir -p "$TARGET"
|
||||
cp -a "$APP_DIR/server.js" "$APP_DIR/package.json" "$APP_DIR/package-lock.json" "$TARGET/" 2>/dev/null || cp -a "$APP_DIR/server.js" "$APP_DIR/package.json" "$TARGET/"
|
||||
[ -f "$APP_DIR/.env" ] && cp "$APP_DIR/.env" "$TARGET/.env" || [ -f "$APP_DIR/.env.example" ] && cp "$APP_DIR/.env.example" "$TARGET/.env" || true
|
||||
chown -R root:root "$TARGET"
|
||||
cd "$TARGET" && npm install --omit=dev
|
||||
cp "$APP_DIR/phoenix-deploy-api.service" /etc/systemd/system/
|
||||
systemctl daemon-reload
|
||||
systemctl enable phoenix-deploy-api
|
||||
systemctl start phoenix-deploy-api
|
||||
echo "Done. Status: $(systemctl is-active phoenix-deploy-api)"
|
||||
echo "Edit $TARGET/.env (GITEA_TOKEN, etc.) and run: systemctl restart phoenix-deploy-api"
|
||||
154
phoenix-deploy-api/server.js
Normal file
154
phoenix-deploy-api/server.js
Normal file
@@ -0,0 +1,154 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Phoenix Deploy API — Gitea webhook receiver and deploy endpoint stub
|
||||
*
|
||||
* Endpoints:
|
||||
* POST /webhook/gitea — Receives Gitea push/tag/PR webhooks
|
||||
* POST /api/deploy — Deploy request (repo, branch, target)
|
||||
*
|
||||
* Env: PORT, GITEA_URL, GITEA_TOKEN, PHOENIX_DEPLOY_SECRET
|
||||
*/
|
||||
|
||||
import crypto from 'crypto';
|
||||
import express from 'express';
|
||||
|
||||
const PORT = parseInt(process.env.PORT || '4001', 10);
|
||||
const GITEA_URL = (process.env.GITEA_URL || 'https://gitea.d-bis.org').replace(/\/$/, '');
|
||||
const GITEA_TOKEN = process.env.GITEA_TOKEN || '';
|
||||
const WEBHOOK_SECRET = process.env.PHOENIX_DEPLOY_SECRET || '';
|
||||
|
||||
const app = express();
|
||||
// Keep raw body for webhook HMAC verification (Gitea uses HMAC-SHA256 of body)
|
||||
app.use(express.json({ verify: (req, _res, buf) => { req.rawBody = buf; } }));
|
||||
|
||||
/**
|
||||
* Update Gitea commit status (pending/success/failure)
|
||||
*/
|
||||
async function setGiteaCommitStatus(owner, repo, sha, state, description, targetUrl = '') {
|
||||
if (!GITEA_TOKEN) return;
|
||||
const url = `${GITEA_URL}/api/v1/repos/${owner}/${repo}/statuses/${sha}`;
|
||||
const body = { state, description, context: 'phoenix-deploy', target_url: targetUrl || undefined };
|
||||
const res = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `token ${GITEA_TOKEN}`,
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
if (!res.ok) {
|
||||
console.error(`Gitea status failed: ${res.status} ${await res.text()}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /webhook/gitea — Gitea webhook receiver
|
||||
* Supports: push, tag, pull_request
|
||||
*/
|
||||
app.post('/webhook/gitea', async (req, res) => {
|
||||
const payload = req.body;
|
||||
if (!payload) {
|
||||
return res.status(400).json({ error: 'No payload' });
|
||||
}
|
||||
|
||||
// Validate X-Gitea-Signature or X-Gogs-Signature (HMAC-SHA256 of raw body, hex)
|
||||
if (WEBHOOK_SECRET) {
|
||||
const sig = req.headers['x-gitea-signature'] || req.headers['x-gogs-signature'];
|
||||
if (!sig) {
|
||||
return res.status(401).json({ error: 'Missing webhook signature' });
|
||||
}
|
||||
const raw = req.rawBody || Buffer.from(JSON.stringify(payload));
|
||||
const expected = crypto.createHmac('sha256', WEBHOOK_SECRET).update(raw).digest('hex');
|
||||
const sigNormalized = String(sig).replace(/^sha256=/, '').trim();
|
||||
const expectedBuf = Buffer.from(expected, 'hex');
|
||||
const sigBuf = Buffer.from(sigNormalized, 'hex');
|
||||
if (expectedBuf.length !== sigBuf.length || !crypto.timingSafeEqual(expectedBuf, sigBuf)) {
|
||||
return res.status(401).json({ error: 'Invalid webhook signature' });
|
||||
}
|
||||
}
|
||||
|
||||
const action = payload.action || (payload.ref ? 'push' : null);
|
||||
const ref = payload.ref || '';
|
||||
const repo = payload.repository;
|
||||
if (!repo) {
|
||||
return res.status(400).json({ error: 'No repository in payload' });
|
||||
}
|
||||
|
||||
const ownerObj = repo.owner || {};
|
||||
const fullName = repo.full_name || `${ownerObj.username || ownerObj.login || 'unknown'}/${repo.name || 'repo'}`;
|
||||
const [owner, repoName] = fullName.split('/');
|
||||
const branch = ref.replace('refs/heads/', '').replace('refs/tags/', '');
|
||||
const pr = payload.pull_request || {};
|
||||
const head = pr.head || {};
|
||||
const sha = payload.after || (payload.sender && payload.sender.sha) || head.sha || '';
|
||||
|
||||
console.log(`[webhook] ${action || 'push'} ${fullName} ${branch} ${sha}`);
|
||||
|
||||
if (action === 'push' || (action === 'synchronize' && payload.pull_request)) {
|
||||
if (branch === 'main' || branch === 'master' || ref.startsWith('refs/tags/')) {
|
||||
if (sha && GITEA_TOKEN) {
|
||||
await setGiteaCommitStatus(owner, repoName, sha, 'pending', 'Phoenix deployment triggered');
|
||||
}
|
||||
// Stub: enqueue deploy; actual implementation would call Proxmox/deploy logic
|
||||
console.log(`[deploy-stub] Would deploy ${fullName} branch=${branch} sha=${sha}`);
|
||||
// Stub: when full deploy runs, call setGiteaCommitStatus(owner, repoName, sha, 'success'|'failure', ...)
|
||||
}
|
||||
}
|
||||
|
||||
res.status(200).json({ received: true, repo: fullName, branch, sha });
|
||||
});
|
||||
|
||||
/**
|
||||
* POST /api/deploy — Deploy endpoint
|
||||
* Body: { repo, branch?, target?, sha? }
|
||||
*/
|
||||
app.post('/api/deploy', async (req, res) => {
|
||||
const auth = req.headers.authorization;
|
||||
if (WEBHOOK_SECRET && auth !== `Bearer ${WEBHOOK_SECRET}`) {
|
||||
return res.status(401).json({ error: 'Unauthorized' });
|
||||
}
|
||||
|
||||
const { repo, branch = 'main', target, sha } = req.body;
|
||||
if (!repo) {
|
||||
return res.status(400).json({ error: 'repo required' });
|
||||
}
|
||||
|
||||
const [owner, repoName] = repo.includes('/') ? repo.split('/') : ['d-bis', repo];
|
||||
const commitSha = sha || '';
|
||||
|
||||
if (commitSha && GITEA_TOKEN) {
|
||||
await setGiteaCommitStatus(owner, repoName, commitSha, 'pending', 'Phoenix deployment in progress');
|
||||
}
|
||||
|
||||
console.log(`[deploy] ${repo} branch=${branch} target=${target || 'default'} sha=${commitSha}`);
|
||||
// Stub: no real deploy yet — report success so Gitea shows green; replace with real deploy + setGiteaCommitStatus on completion
|
||||
const deploySuccess = true;
|
||||
if (commitSha && GITEA_TOKEN) {
|
||||
await setGiteaCommitStatus(
|
||||
owner,
|
||||
repoName,
|
||||
commitSha,
|
||||
deploySuccess ? 'success' : 'failure',
|
||||
deploySuccess ? 'Deploy accepted (stub)' : 'Deploy failed (stub)'
|
||||
);
|
||||
}
|
||||
res.status(202).json({
|
||||
status: 'accepted',
|
||||
repo,
|
||||
branch,
|
||||
target: target || 'default',
|
||||
message: 'Deploy request queued (stub). Implement full deploy logic in Sankofa Phoenix API.',
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /health — Health check
|
||||
*/
|
||||
app.get('/health', (req, res) => {
|
||||
res.json({ status: 'ok', service: 'phoenix-deploy-api' });
|
||||
});
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Phoenix Deploy API listening on port ${PORT}`);
|
||||
if (!GITEA_TOKEN) console.warn('GITEA_TOKEN not set — commit status updates disabled');
|
||||
});
|
||||
Reference in New Issue
Block a user