---
name: ci-deploy
description: >
  Deploy an application to an environment (prod, review, dev) or rollback production.
  Use when the user says /ci-deploy, /deploy, 'deploy en prod', 'deploy en review', 'deploy en dev',
  'deploie', 'rollback', 'rollback prod', 'mets en prod', or wants to deploy a specific version.
argument-hint: "<environment and optional version, e.g. 'prod', 'review', 'dev', 'prod v1.2.3', 'rollback'>"
allowed-tools:
  - Read
  - Glob
  - Grep
  - Bash
  - Skill
  - AskUserQuestion
  - mcp__kubernetes__kubectl_get
  - mcp__kubernetes__kubectl_describe
---

Deploy a Pysae application to an environment or rollback production. Delegates job execution to the `/ci-run` skill.

## Required tools

- `glab`
- `kubectl`

Run `pysae-ai-tools tools require <tools>` before starting; if it exits non-zero, abort and report the install command from stderr to the user.

## Mode detection

Detect the execution context using `detect_context`:
```bash
CTX=$(pysae-ai-tools internal detect-context)
IS_CI=$(echo "$CTX" | jq -r '.is_ci')
PROJECT_ID=$(echo "$CTX" | jq -r '.project_id // empty')
PROJECT_PATH=$(echo "$CTX" | jq -r '.project_path // empty')
```

In CI mode (`IS_CI` is `true`):
- **Never use `AskUserQuestion`** — all required parameters (environment, version, app) must come from `$ARGUMENTS`
- **Skip production/rollback confirmations** — proceed directly if arguments are explicit
- If a required argument is missing, **fail with exit code 1** and print the missing parameter to stdout

## Step 1 : Target repository resolution

If the user mentions a specific application (e.g. "deploy op en prod", "deploy driver", "deploy l'API"), resolve the target repository using the application table in [../references/pysae-projects.md](../references/pysae-projects.md) (section "Application repositories"). Extract the `GitLab Project ID` and `K8s deployment` name.

**Default**: if no application is specified, assume `pysae/api`.

Set `PROJECT_ID` accordingly. When operating on a different repo than the current working directory, use the project ID directly with the API.

## Step 2 : Check redeploy support

For production and rollback scenarios, check if the project supports the fast `redeploy` job:
```bash
CI_YAML=$(glab api "projects/${PROJECT_ID}/repository/files/.gitlab-ci.yml/raw?ref=main" 2>/dev/null)
```
Check if the YAML includes `redeploy.auto.yml` (from `pysae/infra/gitlab-templates`). Store the result as `SUPPORTS_REDEPLOY` (true/false) for use in Scenario A and B.

If `redeploy.auto.yml` is not included, the project does not support fast redeploy — always use `deploy_prod`.

## Step 3 : Scenario detection

Interpret `$ARGUMENTS` to determine the deployment scenario:

### Scenario A — Deploy to production

**Triggers**: `deploy prod`, `deploie en prod`, `mets en prod`, `prod`

1. **Find the target tag** — use the latest tag by default, or the version specified by the user:
   ```bash
   # Latest tag
   TAG=$(glab api "projects/${PROJECT_ID}/repository/tags?per_page=1" | jq -r '.[0].name')
   # Or user-specified: tag name, commit hash, or version number
   ```

2. **Find the pipeline for that tag**:
   ```bash
   PIPELINE_ID=$(glab api "projects/${PROJECT_ID}/pipelines?ref=${TAG}&per_page=1" | jq -r '.[0].id')
   ```
   If no pipeline exists for this tag, create one:
   ```bash
   glab api -X POST "projects/${PROJECT_ID}/pipeline" -f "ref=${TAG}"
   ```

3. **Choose the deploy job — prefer `redeploy` when the project supports it**:

   If `SUPPORTS_REDEPLOY` is true, list jobs in the pipeline and check statuses:
   ```bash
   JOBS=$(glab api "projects/${PROJECT_ID}/pipelines/${PIPELINE_ID}/jobs?per_page=100&include_retried=false")
   HAS_REDEPLOY=$(echo "$JOBS" | jq '[.[] | select(.name == "redeploy")] | length > 0')
   DEPLOY_PROD_STATUS=$(echo "$JOBS" | jq -r '[.[] | select(.name == "deploy_prod")][0].status // empty')
   ```

   - If `SUPPORTS_REDEPLOY` is true **and** `redeploy` job exists in the pipeline **and** `deploy_prod` has already succeeded (status `success`) → **use `redeploy`** as target job. This is much faster: it only updates the git ref and image tag on the ArgoCD app, without rebuilding or retesting.
   - Otherwise → use `deploy_prod` as target job (standard full deploy).

   Inform the user which path is taken:
   > Utilisation du job `redeploy` (deploy rapide, sans rebuild) — `deploy_prod` déjà en succès sur cette pipeline.

   or:
   > Utilisation du job `deploy_prod` (deploy complet avec build et tests).

4. **Confirm with the user** — production deployments require explicit confirmation:
   > Deploy de `<TAG>` en production pour `<app>`. Tu confirmes ?

5. Invoke `/ci-run` with the chosen target job and pipeline context.

### Scenario B — Rollback production

**Triggers**: `rollback`, `rollback prod`, `reviens en arriere`

1. **Find the current running version** on the EKS prod cluster:
   ```bash
   kubectl get deployment <k8s-deployment-name> -n pysae-prod \
     -o jsonpath='{.spec.template.spec.containers[0].image}'
   ```
   Extract the image tag from the result.

2. **Find the previous working version** — list recent tags and identify the one before the current:
   ```bash
   glab api "projects/${PROJECT_ID}/repository/tags?per_page=10" | jq -r '.[].name'
   ```
   If the user specifies a target version, use that instead.

3. **Find the pipeline for the rollback tag** — same as Scenario A step 2.

4. **Choose the deploy job — prefer `redeploy`** — same logic as Scenario A step 3: if `SUPPORTS_REDEPLOY` is true, `redeploy` exists and `deploy_prod` already succeeded on the rollback tag's pipeline, use `redeploy`. This is the typical rollback case since the previous version was already deployed once.

5. **Confirm with the user** — rollbacks are critical operations:
   > Rollback de `<current_version>` vers `<rollback_version>` sur la prod pour `<app>`. Tu confirmes ?

6. Invoke `/ci-run` with the chosen job on the rollback tag's pipeline.

### Scenario C — Deploy to review

**Triggers**: `deploy review`, `deploie en review`

1. **Pipeline context** — use `MR_IID` from `detect_context` (resolved in Mode detection).
   If no MR exists (`MR_IID` is empty), inform the user and stop.

2. **Target job**: `deploy_review`

3. Invoke `/ci-run` with the target job.

### Scenario D — Deploy to dev

**Triggers**: `deploy dev`, `deploie en dev`

1. **Pipeline context** — use the pipeline for the `main` branch (or user-specified ref):
   ```bash
   PIPELINE_ID=$(glab api "projects/${PROJECT_ID}/pipelines?ref=main&per_page=1" | jq -r '.[0].id')
   ```
   If the user specifies a version (tag, commit hash), use that ref instead of `main`.

2. **Target job**: the deploy job for the dev environment (check `.gitlab-ci.yml` for the exact name — usually `deploy_dev` or similar)

3. Invoke `/ci-run` with the target job and pipeline context.

## Step 4 : Execute via /ci-run

Once the scenario is resolved (target job, pipeline, and project), delegate execution to the `/ci-run` skill. Pass the exact job name so `/ci-run` handles dependency resolution and job execution.

If `/ci-run` reports a failure, propose to run `/ci-debug` to diagnose the issue.

## Step 5 : Report

After the deployment job completes:

- Confirm the deployed version and environment
- Include the pipeline and job links
- **Always fetch and display the environment URL** from the deployment job:
  ```bash
  glab api "projects/${PROJECT_ID}/pipelines/${PIPELINE_ID}/jobs?per_page=100" | \
    jq -r '.[] | select(.name == "<deploy_job_name>") | .environment.external_url // empty'
  ```
  If available, display it prominently — this is the URL the user expects when they ask for it

## Rules

- **Production and rollback require explicit user confirmation** — never deploy to prod or rollback without asking (local mode only; CI mode skips confirmation)
- **Review and dev do not require confirmation** — execute directly
- **Default app is API** — if the user doesn't specify which app, assume `pysae/api`
- **Language**: French for communication, English for commands
- **Delegate to /ci-run** for all job execution — do not duplicate job triggering logic
- If `glab` is not available or fails, inform the user and stop

$ARGUMENTS
