Pipeline gets broken for no reason

Describe your question in as much detail as possible:
I think I have some misunderstanding of “needs:” keyword in Gitlab CI.
Here is a part of my gitlab-ci.yml:

stages:
  - build_latest
  - build_tagged

latest_build:
  stage: build_latest
  rules:
    - if: '$CI_COMMIT_BRANCH == "master"'
      when: on_success
  script:
    - |
      docker build --pull -t "$CI_REGISTRY_IMAGE:latest" .
      docker push "$CI_REGISTRY_IMAGE:latest"

tagged_build:
  stage: build_tagged
  needs:
    - latest_build
  rules:
    - if: '$CI_COMMIT_TAG =~ /^v\d+.\d+.\d+/'
  script:
    - |
      docker pull "$CI_REGISTRY_IMAGE:latest"
      docker tag "$CI_REGISTRY_IMAGE:latest" "$CI_REGISTRY_IMAGE:$CI_COMMIT_TAG"
      docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_TAG"

The goal is to build image with “latest” tag, then, if this stage succeeded and the latest commit is associated with git tag, to tag docker image accordingly. However, I’m getting a YAML error: “tagged_build: needs ‘latest_build’”

And I don’t understand why this happens, because “build_latest” succeeds and “build_tagged” gets cancelled immediately on push:

Most likely my error is explained at https://docs.gitlab.com/ee/ci/yaml/#requirements-and-limitations: " If needs: is set to point to a job that is not instantiated because of only/except rules or otherwise does not exist, the pipeline is not created and a YAML error is shown.", however, “build_latest” job does run and even finish successfully.

I’m running a self-hosted “free” Gitlab instance, version 13.5.3

So, I started digging into the problem and first I decided to replace my gitlab-ci.yml file with this: https://gitlab.com/mlapierre-test/test/blob/needs/.gitlab-ci.yml

linux:build:
  stage: build
  script: echo "done"

mac:build:
  stage: build
  script: echo "done"

linux:rspec:
  stage: test
  script: echo "done"
  needs: ["linux:build"]

linux:rubocop:
  stage: test
  script: echo "done"
  needs: ["linux:build"]

mac:rspec:
  stage: test
  script: echo "done"
  needs: ["mac:build"]

mac:rubocop:
  stage: test
  script: echo "done"
  needs: ["mac:build"]

production:
  stage: deploy
  script: echo "done"

It worked fine. Then I started removing extra steps and replacing them with my old code. Here is the last successful pipeline:

stages:
  - build_latest
  - build_tagged

build_latest:
  stage: build_latest
  rules:
    - if: '$CI_COMMIT_BRANCH == "master"'
      when: on_success
  script:
    - |
      docker build --pull -t "$CI_REGISTRY_IMAGE:latest" .
      docker push "$CI_REGISTRY_IMAGE:latest"


build_tagged:
  stage: build_tagged
  script: echo "done"
  needs: ["build_latest"]

However when I replaced “build_tagged” contents with actual code:

build_tagged:
  stage: build_tagged
  needs: ["build_latest"]
  rules:
    - if: '$CI_COMMIT_TAG =~ /^v\d+.\d+.\d+/'
  script:
    - |
      docker pull "$CI_REGISTRY_IMAGE:latest"
      docker tag "$CI_REGISTRY_IMAGE:latest" "$CI_REGISTRY_IMAGE:$CI_COMMIT_TAG"
      docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_TAG"

Everything got broken again. As you can see, the pipeline gets split into parts.

And this change breaks everything for an unknown reason:

I think I’ve got an idea. “build_tagged” job throws an error, because it needs “build_latest”, but “build_latest” doesn’t use the same trigger (tag) as “build_tagged”.

Is there any workaround how to run “build_tagged” only if “build_latest” succeed?

rules:
    on_success

might do it?

@snim2
I think on_success is implicitly used as default value, isn’t it?

Of course! You’re quite right.

@snim2 Thank you for your reply. Unfortunately, I’m afraid I didn’t understand you. I already knew that on_success is the default value, but I’m not sure how explicitly adding it would help me resolve the problem.
Could you please explain a bit further if you don’t mind?