Communitygithub.com

Publication sur App Platform

Publier un site web ou une application sur DigitalOcean App Platform et renvoyer son URL publique. À utiliser chaque fois que l'utilisateur demande de publier, déployer, héberger ou mettre en ligne quelque chose que vous avez construit.

Qu'est-ce que Publication sur App Platform ?

Cette compétence automatise le déploiement d'applications et de sites web sur DigitalOcean App Platform, en évitant les outils Docker ou doctl non disponibles. Elle couvre le cycle complet : construction et test dans le sandbox, création d'un dépôt GitHub sur le compte partagé `jnaidu-do`, création de l'application App Platform, attente du déploiement actif, et correction des erreurs à partir des logs de build. Supporte les sites statiques (HTML/CSS/JS, Vite, CRA, Astro) ainsi que les services avec serveur (Node, Python) et peut configurer une base de données PostgreSQL managée si nécessaire. L'utilisateur doit approuver chaque étape : la création du dépôt, le push, la création de l'application, et toute modification ultérieure. La compétence utilise un courtier d'identifiants pour invoquer les API DigitalOcean et GitHub sans exposer les tokens, et gère les erreurs comme les certificats SSL PostgreSQL ou les accès refusés aux dépôts GitHub.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/jnaidu-do/qm-appplatform-skills/tree/main/app-platform-publish

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

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)

IdentifiantHôteAutorisé
digitaloceanapi.digitalocean.comGET/POST/PUT/DELETE sous /v2/apps et /v2/regions
githubapi.github.comGET/POST/PATCH sous /user et /repos/jnaidu-do
github-gitgithub.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éfinit PORT ; 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écessite package.json avec un script start ; Python nécessite requirements.txt et une commande d'exécution (par ex. gunicorn app:app --bind 0.0.0.0:$PORT). Un Dockerfile dans le dépôt fonctionne aussi (définissez dockerfile_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 avec PORT=8080, et curl localhost:8080. Ne publiez que ce qui fonctionne.
  • Ajoutez un .gitignore avec node_modules etc. 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 :

  1. Ce que vous avez construit et qu'il s'est exécuté localement (une ligne).
  2. 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égion nyc : 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
  3. Ce qui devient public : le dépôt et le site en ligne sont lisibles par n'importe qui sur Internet.
  4. 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 :

SignalCorrectif
Missing script: start / commande introuvablebuild_command / run_command erroné, ou absence du script start
Le conteneur se ferme, la vérification de santé échouen'écoute pas sur $PORT / n'est pas lié à 0.0.0.0, ou plantage au démarrage
SELF_SIGNED_CERT_IN_CHAINAide SSL Postgres manquante (voir ci-dessus)
Cannot find moduledé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 secondesla 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 »).

Skills associés