<p>I have a kubernetes ingress manifest YAML, looking like next:</p>

<pre><code>apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    certmanager.k8s.io/acme-http01-edit-in-place: "true"
    certmanager.k8s.io/cluster-issuer: letsencrypt
  name: staging
  namespace: dev
spec:
  rules:
  - host: staging.domain.com
    http:
      paths:
      - backend:
          serviceName: task-11111
          servicePort: 80
        path: /task-11111/*
      - backend:
          serviceName: task-22222
          servicePort: 80
        path: /task-22222/*
      - backend:
          serviceName: task-33333
          servicePort: 80
        path: /task-33333/*
  tls:
  - hosts:
    - staging.domain.com
    secretName: staging-domain-com
</code></pre>

<p>What I'm trying to achieve is to add (if not present) or remove (if present) a backend block. What I have now:</p>

<pre><code>yq -y '.spec.rules[].http.paths += [{ "backend": { "serviceName": "'${CI_COMMIT_REF_NAME}'", "servicePort": 80}, "path": "/'${CI_COMMIT_REF_NAME}'/*"}]'
</code></pre>

<p>(adds a new block with variable value, but doesn't bother if it already exists)</p>

<pre><code>yq -y 'del(.. | .paths? // empty | .[] | select(.path |contains("'${CI_COMMIT_REF_NAME}'")) )'
</code></pre>

<p>(fails with <code>jq: error (at &lt;stdin&gt;:0): Invalid path expression with result {"backend":{"serviceName":...</code>)</p>

<p>So rules may look like this after deletion (assume that <code>CI_COMMIT_REF_NAME</code> = <code>task-33333</code>):</p>

<pre><code>spec:
  rules:
  - host: staging.domain.com
    http:
      paths:
      - backend:
          serviceName: task-11111
          servicePort: 80
        path: /task-11111/*
      - backend:
          serviceName: task-22222
          servicePort: 80
        path: /task-22222/*
</code></pre>

<p>or like this after adding (assume that <code>CI_COMMIT_REF_NAME</code> = <code>task-44444</code>):</p>

<pre><code>spec:
  rules:
  - host: staging.domain.com
    http:
      paths:
      - backend:
          serviceName: task-11111
          servicePort: 80
        path: /task-11111/*
      - backend:
          serviceName: task-22222
          servicePort: 80
        path: /task-22222/*
      - backend:
          serviceName: task-33333
          servicePort: 80
        path: /task-33333/*
      - backend:
          serviceName: task-44444
          servicePort: 80
        path: /task-44444/*
</code></pre>

<p>Any help is greatly appreciated.  </p>

d.ansimov
<p>[The following has been revised to reflect the update to the question.]</p>

<p>Assuming CI_COMMIT_REF_NAME is available to jq as $CI_COMMIT_REF_NAME, which can be done using jq with the command-line argument:</p>

<pre><code>--arg CI_COMMIT_REF_NAME "$CI_COMMIT_REF_NAME"
</code></pre>

<p>an appropriate jq filter would be along the following lines:</p>

<pre><code>.spec.rules[0].http.paths |=
  (map(select(.path | index($CI_COMMIT_REF_NAME) | not)) as $new
   | if ($new | length) == length
     then . + [{ "backend": { "serviceName": $CI_COMMIT_REF_NAME, "servicePort": 80}, "path": ($CI_COMMIT_REF_NAME + "/*") }]
     else $new
     end )
</code></pre>

<p>You can test this with the following jq invocation:</p>

<pre><code>jq --arg CI_COMMIT_REF_NAME task-4444 -f program.jq input.json
</code></pre>

<p>where of course <code>input.json</code> is the JSON version of your YAML.</p>

<p>(I'd use <code>index</code> in preference to <code>contains</code> if at all possible.)</p>
