generateDeployScript method
Generate deployment script
Implementation
Future<void> generateDeployScript() async {
if (!config.createServer) return;
if (config.firebaseProjectId == null) {
warn('Firebase project ID not set, skipping deploy script');
return;
}
info('Generating deploy script...');
final String content =
'''
#!/bin/bash
# Deployment script for ${config.serverPackageName}
#
# Generated by Oracular. Idempotent: every gcloud step gracefully handles
# already-existing resources. Cleanup steps at the end keep Artifact
# Registry storage and Cloud Run revision counts bounded.
set -e
PROJECT_ID="${config.firebaseProjectId}"
REGION="us-central1"
SERVICE_NAME="${config.serverPackageName.replaceAll('_', '-')}"
REPOSITORY="oracular"
IMAGE_NAME="\$REGION-docker.pkg.dev/\$PROJECT_ID/\$REPOSITORY/\$SERVICE_NAME"
# Cleanup tunables — keep in sync with oracular/lib/models/setup_config.dart
ARTIFACT_KEEP_RECENT="${config.artifactKeepRecent}"
ARTIFACT_DELETE_OLDER_DAYS="${config.artifactDeleteOlderDays}"
CLOUD_RUN_KEEP_REVISIONS="${config.cloudRunKeepRevisions}"
echo "==> Ensuring Artifact Registry repository exists..."
gcloud artifacts repositories create \$REPOSITORY \\
--repository-format=docker \\
--location \$REGION \\
--project \$PROJECT_ID || true
echo "==> Configuring Docker auth for Artifact Registry..."
gcloud auth configure-docker "\$REGION-docker.pkg.dev" --quiet
echo "==> Building Docker image..."
docker build --platform linux/amd64 -t \$IMAGE_NAME .
echo "==> Pushing to Artifact Registry..."
docker push \$IMAGE_NAME
echo "==> Deploying to Cloud Run..."
gcloud run deploy \$SERVICE_NAME \\
--image \$IMAGE_NAME \\
--platform managed \\
--region \$REGION \\
--project \$PROJECT_ID \\
--allow-unauthenticated \\
--port 8080 \\
--memory 512Mi \\
--cpu 1 \\
--min-instances 0 \\
--max-instances 10
# ─── Cleanup: Artifact Registry image versions ──────────────────────────────
# Apply the cleanup-policy.json shipped alongside this script. Keeps the
# N most-recent versions and deletes anything older than D days.
if [ -f "\$(dirname "\$0")/cleanup-policy.json" ]; then
echo "==> Applying Artifact Registry cleanup policy..."
gcloud artifacts repositories set-cleanup-policies \$REPOSITORY \\
--location \$REGION \\
--project \$PROJECT_ID \\
--policy "\$(dirname "\$0")/cleanup-policy.json" || \\
echo "WARNING: cleanup policy not applied (gcloud >= 444.0.0 required)"
else
echo "WARNING: cleanup-policy.json not found alongside script — skipping AR cleanup"
fi
# ─── Cleanup: Cloud Run revisions ───────────────────────────────────────────
# Keep the latest \$CLOUD_RUN_KEEP_REVISIONS revisions and delete anything
# older that is not currently routing traffic.
echo "==> Pruning old Cloud Run revisions for \$SERVICE_NAME (keep latest \$CLOUD_RUN_KEEP_REVISIONS)..."
ALL_REVS=\$(gcloud run revisions list \\
--service=\$SERVICE_NAME \\
--region=\$REGION \\
--project=\$PROJECT_ID \\
--sort-by="~creationTimestamp" \\
--format="value(metadata.name)" 2>/dev/null || true)
if [ -n "\$ALL_REVS" ]; then
TRAFFIC_REVS=\$(gcloud run services describe \$SERVICE_NAME \\
--region=\$REGION \\
--project=\$PROJECT_ID \\
--format="value(status.traffic.revisionName)" 2>/dev/null || true)
COUNT=0
for REV in \$ALL_REVS; do
COUNT=\$((COUNT+1))
if [ "\$COUNT" -le "\$CLOUD_RUN_KEEP_REVISIONS" ]; then
continue
fi
if echo "\$TRAFFIC_REVS" | grep -q "\$REV"; then
echo " Skipping \$REV (serving traffic)"
continue
fi
echo " Deleting old revision: \$REV"
gcloud run revisions delete \$REV \\
--region=\$REGION \\
--project=\$PROJECT_ID \\
--quiet || echo " WARNING: could not delete \$REV"
done
else
echo "WARNING: could not list Cloud Run revisions — skipping prune"
fi
echo ""
echo "Deployment complete!"
echo "Service URL: https://\$SERVICE_NAME-\$PROJECT_ID.\$REGION.run.app"
''';
final File file3 = File(p.join(serverPath, 'script_deploy.sh'));
await file3.writeAsString(content);
// Make executable
await _runner.run('chmod', <String>['+x', file3.path]);
success('Generated: ${config.serverPackageName}/script_deploy.sh');
// Also drop the cleanup-policy.json next to the script so the script
// can find it without depending on the templates directory at runtime.
await _ensureCleanupPolicyFile();
}