Publier sur App Platform
Flux : construire et tester dans le sandbox → rendre déployable → proposer le plan et obtenir un oui → pousser sur GitHub → créer l'application App Platform → attendre ACTIF → vérifier l'URL.
Ne créez jamais de dépôt, ne poussez pas, ne créez/mettez à jour/supprimez pas d'application avant que l'utilisateur n'ait approuvé le plan à l'étape 2. Une demande comme « publie-le » n'est pas cette approbation. Construire et tester dans le sandbox ne nécessite pas d'approbation.
N'utilisez jamais l'action apps publish, docker, ou doctl — rien de tout cela ne fonctionne sur cet ordinateur. Tout passe par
deux identifiants partagés, que vous appelez via le courtier d'identifiants du noyau. Vous ne voyez jamais les jetons.
Utilisez un sandbox_id explicite pour chaque commande, et sélectionnez l'identifiant dans execute.credentials pour toute commande
qui appelle le courtier (digitalocean, github, ou github-git). Le noyau fournit AGENT_CREDENTIAL_TOKEN à ces
commandes uniquement. N'utilisez pas rm -rf ; créez plutôt un répertoire frais avec mktemp -d.
Ce que l'ordinateur a
Node 24 + npm, Python 3.12 + pip, git, gh (non connecté ; ne l'utilisez pas), curl, gcc, make. Pas de docker, doctl, jq,
wget, unzip. Analysez le JSON avec node -e ou python3 -c.
Identifiants (ce que chacun peut faire)
| Identifiant | Hôte | Autorisé |
|---|---|---|
digitalocean | api.digitalocean.com | GET/POST/PUT/DELETE sous /v2/apps et /v2/regions |
github | api.github.com | GET/POST/PATCH sous /user et /repos/jnaidu-do |
github-git | github.com (git push/clone) | GET/POST sous /jnaidu-do/ |
Tout est créé sous le compte GitHub partagé jnaidu-do (un identifiant à l'échelle du déploiement, pas le compte de l'utilisateur).
Dites-le dans le plan. Si l'utilisateur veut son propre compte, dites-lui que ce n'est pas encore configuré. Une erreur 403 path_not_allowed / method_not_allowed signifie que la
requête est en dehors de cela — n'essayez pas de variantes ; dites à l'utilisateur que ce n'est pas autorisé.
Aide du courtier
Collez ceci une fois par commande (l'état du shell ne persiste pas). Il affiche le statut HTTP sur stderr et le corps de la réponse sur stdout :
bk() { # bk <identifiant> <MÉTHODE> <url> [corps-json]
p=$(node -e 'const [c,m,u,b]=process.argv.slice(1);console.log(JSON.stringify({credential:c,method:m,url:u,headers:{accept:"application/json","content-type":"application/json"},...(b?{body:b}:{})}))' "$1" "$2" "$3" "${4:-}")
curl -fsS -X POST "$AGENT_API_URL/v1/credentials/broker" -H "x-agent-capability: $AGENT_CREDENTIAL_TOKEN" \
-H "content-type: application/json" -d "$p" |
node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{const r=JSON.parse(s);console.error("HTTP "+r.status);console.log(r.body)})'
}
1. Rendre déployable (vérifier avant de pousser)
L'application s'exécute comme un conteneur normal derrière App Platform, pas dans ce sandbox.
- Écoutez sur
$PORT, lié à0.0.0.0(App Platform définitPORT; par défaut 8080). Ne codez jamais en dur un autre port. - HTML/CSS/JS simple ou un framework qui compile en fichiers statiques (Vite, CRA, Astro) : déployez comme site statique
(
build_command,output_dir). Préférez cela à l'exécution d'un serveur pour une page statique. - Tout ce qui a un serveur : un service avec
build_command,run_command,http_port. Les dispositions standard fonctionnent sans configuration : Node nécessitepackage.jsonavec un scriptstart; Python nécessiterequirements.txtet une commande d'exécution (par ex.gunicorn app:app --bind 0.0.0.0:$PORT). UnDockerfiledans le dépôt fonctionne aussi (définissezdockerfile_path), mais vous ne pouvez pas le tester en build ici, donc restez simple. - Pas de disque persistant. Tout ce qui est écrit sur le système de fichiers est perdu à chaque déploiement. N'utilisez pas de fichiers SQLite, de téléchargements sur disque, ou de fichiers JSON pour l'état. Si l'application a besoin d'une base de données, ajoutez un Postgres managé (voir ci-dessous) ; sinon gardez-la sans état.
- Exécutez-la d'abord localement :
npm install, démarrez-la avecPORT=8080, etcurl localhost:8080. Ne publiez que ce qui fonctionne. - Ajoutez un
.gitignoreavecnode_modulesetc. Ne validez jamais de secrets. N'imprimez jamais de jetons.
Si l'application utilise Postgres
${db.DATABASE_URL} d'App Platform inclut sslmode=require, et Node pg rejette alors le CA managé
(SELF_SIGNED_CERT_IN_CHAIN). Supprimez sslmode de l'URL et définissez ssl: { rejectUnauthorized: false } :
function normalizeDatabaseUrl(raw) {
const u = new URL(raw);
u.searchParams.delete("sslmode"); u.searchParams.delete("ssl");
return u.toString();
}
const pool = new Pool({ connectionString: normalizeDatabaseUrl(process.env.DATABASE_URL), ssl: { rejectUnauthorized: false } });
Ajoutez "databases": [{"name":"db","engine":"PG","production":false,"version":"16"}] à la spécification et
{"key":"DATABASE_URL","scope":"RUN_TIME","value":"${db.DATABASE_URL}"} à chaque composant qui en a besoin. Réessayez la
première connexion pendant environ 30 s : la base de données peut encore être en démarrage. N'ajoutez une base de données que si l'application en a
vraiment besoin (cela coûte plus cher).
2. Proposer le plan et demander (arrêtez-vous ici jusqu'à ce qu'ils disent oui)
Une fois que l'application se construit et s'exécute localement, arrêtez-vous et envoyez à l'utilisateur un court plan, puis attendez une réponse. N'exécutez aucun
appel bk, git push, ou création de dépôt/application jusqu'à ce qu'ils approuvent. Incluez :
- Ce que vous avez construit et qu'il s'est exécuté localement (une ligne).
- Les étapes que vous allez suivre, dans l'ordre, chacune avec ce qu'elle touche :
- Créer le dépôt GitHub
jnaidu-do/<nom>— public, sur le compte partagéjnaidu-do(pas le compte de l'utilisateur) - Pousser les fichiers de l'application avec git (listez ce qui est inclus ; confirmez qu'il n'y a pas de secrets)
- Créer l'application App Platform
<nom>dans la régionnyc: un site statique (gratuit pour quelques-uns) ou un service (basic-xxs, environ 5 $/mois) — dites lequel, et le coût - Attendre la construction (3–5 min), puis donner l'URL publique
- Créer le dépôt GitHub
- Ce qui devient public : le dépôt et le site en ligne sont lisibles par n'importe qui sur Internet.
- Ce que vous ne ferez pas sans redemander : pousser d'autres modifications, modifier la spécification ou supprimer quoi que ce soit.
Terminez par une question directe, par ex. « Dois-je créer le dépôt et l'application comme ci-dessus ? Vous pouvez aussi changer le nom, rendre le dépôt privé (cela nécessite l'octroi de l'application GitHub DO), ou dire non. » Proposez des alternatives si pertinent (statique vs service, nom différent). S'ils refusent, donnez-leur les fichiers ou le code prêt pour le dépôt et arrêtez-vous.
Redemandez, séparément, avant : tout git push ultérieur (mises à jour), modification de la spécification de l'application, et suppression de l'application.
3. Pousser sur GitHub
Utilisez le nom proposé à l'étape 2 (qm-<court-nom-kebab>) (minuscules). Si le nom est pris (422), ajoutez un suffixe court.
# créer le dépôt (public : App Platform peut ensuite le cloner sans authentification supplémentaire)
bk github POST https://api.github.com/user/repos '{"name":"qm-hello-site","private":false}'
# pousser depuis un répertoire frais et propre contenant l'application (pas de node_modules)
cd "$(mktemp -d)" && cp -r /path/to/app/. . && git init -q -b main && git add . &&
git -c user.name=QM -c [email protected] commit -qm "Initial commit" &&
git remote add origin "$AGENT_API_URL/v1/credentials/git/github-git/jnaidu-do/qm-hello-site.git" &&
git -c http.extraHeader="x-agent-capability: $AGENT_CREDENTIAL_TOKEN" push origin HEAD:refs/heads/main
Mises à jour ultérieures : validez et poussez de la même manière (HEAD:refs/heads/main), mais seulement après avoir informé l'utilisateur de ce qui a changé et
obtenu un oui — chaque push est une nouvelle approbation, non couverte par le premier plan.
4. Créer l'application App Platform
bk digitalocean POST https://api.digitalocean.com/v2/apps '{"spec": {
"name": "qm-hello-site",
"region": "nyc",
"services": [{
"name": "web",
"github": {"repo": "jnaidu-do/qm-hello-site", "branch": "main", "deploy_on_push": true},
"build_command": "npm install",
"run_command": "npm start",
"http_port": 8080,
"instance_size_slug": "basic-xxs",
"instance_count": 1
}]
}}'
Au lieu de services pour un site statique : "static_sites": [{"name":"web","github":{...},"build_command":"npm run build","output_dir":"dist"}]
(HTML simple : omettez build_command, output_dir est "/" ; pour une application monopage, ajoutez
"catchall_document":"index.html").
La réponse contient l'id de l'application. Gardez-le. Les noms d'application sont en lettres minuscules, chiffres, tirets, 2–32 caractères.
Si la création échoue avec une erreur d'accès au dépôt GitHub (« does not have access to the repository ») : l'application GitHub DigitalOcean n'a pas accès à ce dépôt.
Dites à l'utilisateur d'ouvrir
https://github.com/apps/digitalocean/installations/select_target et d'accorder l'accès aux dépôts jnaidu-do, puis dites que c'est fait ;
réessayez la même requête. S'il ne peut pas, utilisez une source de clonage public générique
("git": {"repo_clone_url": "https://github.com/jnaidu-do/qm-hello-site.git", "branch": "main"}) et dites-lui que les mises à jour
nécessiteront alors un redéploiement (voir ci-dessous).
5. Attendre ACTIF
Une vraie construction prend 3 à 5 minutes. Interrogez toutes les 30 s et dites à l'utilisateur que vous attendez :
bk digitalocean GET "https://api.digitalocean.com/v2/apps/$APP_ID/deployments?per_page=1" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{const d=JSON.parse(s).deployments[0];console.log(d.id,d.phase,d.progress?.success_steps+"/"+d.progress?.total_steps)})'
Phases : PENDING_BUILD → BUILDING → DEPLOYING → ACTIVE. ERROR ou CANCELED signifie un échec ; allez à
« En cas d'échec ». Arrêtez d'interroger après environ 12 minutes et dites à l'utilisateur où cela en est.
6. Signaler l'URL
bk digitalocean GET "https://api.digitalocean.com/v2/apps/$APP_ID" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>console.log(JSON.parse(s).app.live_url))'
Essayez curl -s -o /dev/null -w '%{http_code}' <live_url> — attendez-vous à 200. Cet ordinateur ne peut souvent pas atteindre les nouveaux
sites *.ondigitalocean.app (échec de connexion). Si curl échoue mais que le déploiement est ACTIF, ce n'est pas un échec de déploiement :
dites que vous n'avez pas pu vérifier depuis ici et demandez à l'utilisateur d'ouvrir l'URL. Donnez à l'utilisateur l'URL complète https://…ondigitalocean.app
et l'URL du dépôt. L'application est publique sur Internet.
En cas d'échec
Lisez les logs vous-même ; ne les demandez pas à l'utilisateur. Obtenez une URL de log, puis récupérez-la :
bk digitalocean GET "https://api.digitalocean.com/v2/apps/$APP_ID/deployments/$DEPLOYMENT_ID/logs?type=BUILD"
# le corps contient "live_url" (ou "historic_urls") ; faites un curl sur cette URL (aucune authentification nécessaire)
type est BUILD, DEPLOY, ou RUN (les sites statiques n'ont que BUILD). Faites correspondre le signal :
| Signal | Correctif |
|---|---|
Missing script: start / commande introuvable | build_command / run_command erroné, ou absence du script start |
| Le conteneur se ferme, la vérification de santé échoue | n'écoute pas sur $PORT / n'est pas lié à 0.0.0.0, ou plantage au démarrage |
SELF_SIGNED_CERT_IN_CHAIN | Aide SSL Postgres manquante (voir ci-dessus) |
Cannot find module | dépendance manquante dans package.json, ou node_modules validé incohérent |
| Vite « Blocked request. This host is not allowed » | définissez preview: { allowedHosts: true } (ou servez le dist construit comme site statique) |
| Build « skipped » / ACTIF en secondes | la source n'a pas changé ; poussez un vrai commit |
Dites à l'utilisateur la cause racine (la ligne de log exacte) et le correctif que vous proposez, et demandez avant de pousser le correctif ou de modifier
la spécification — chaque push et chaque modification de spécification nécessite son propre oui. Après approbation, corrigez le code, validez et poussez
(deploy_on_push redéploie automatiquement), puis interrogez à nouveau. Pour une modification de spécification, envoyez
PUT /v2/apps/$APP_ID avec {"spec": {...}} (obtenez d'abord la spécification actuelle avec GET /v2/apps/$APP_ID et modifiez-la).
Pour une source git générique, forcez une reconstruction avec POST /v2/apps/$APP_ID/deployments corps {"force_build":true}.
Ne supprimez pas et ne recréez pas l'application pour corriger un échec — cela change l'URL. Corrigez en avant. Abandonnez après trois
tentatives échouées différentes et rapportez ce que vous avez essayé. Lire les logs ne nécessite pas d'approbation.
Suppression
Uniquement lorsque l'utilisateur le demande explicitement, et nommez d'abord l'application. DELETE /v2/apps/$APP_ID supprime l'application. Le dépôt GitHub
est laissé en place (cet identifiant ne peut pas supprimer de dépôts) ; dites-le.
Rapport
Terminez par : l'URL en direct, l'URL du dépôt, et une ligne sur comment le mettre à jour (« demandez-moi de changer X et je le pousserai »).