Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(Some) Anchors Do Not Work - Raises Invalid YAML #14

Closed
acederberg opened this issue Apr 8, 2022 · 12 comments
Closed

(Some) Anchors Do Not Work - Raises Invalid YAML #14

acederberg opened this issue Apr 8, 2022 · 12 comments
Labels
accessibility easy access and use of pipeline features

Comments

@acederberg
Copy link

acederberg commented Apr 8, 2022

Thanks for the awesome tool, it has/will save me a lot of time. I was running this on a new pipeline like

cd <my_project>
pipelines

and it says

bitbucket-pipelines.yml; verify the file contains valid YAML

But I can parse the file with python

from yaml import safe_load
with open( 'bitbucket-pipelines.yml', 'r' )  as file : safe_load( file )

and it passes in the bitbucket validator. When I run the pipeline on bitbucket it works. I can provide my yaml, but it will require some amendments as this is internal to my organization and thus I am hesitant. If I figure out what is making the parser upset I will add that to my post here.

It also might be worth considering that I am using the windows subsystem for linux.

Thank you again for any time, it is really appreciated.

EDIT : Debug output

Running pipelines --debug gets

pipelines: file parse error: YAML error: /mnt/c/MVE/.../bitbucket-pipelines.yml; verify the file contains valid YAML
pipelines: version 0.0.62-composer w/ php 7.4.3 (libyaml: n/a)
--------
class....: Ktomk\Pipelines\File\ParseException
message..: file parse error: YAML error: /mnt/c/.../<my_project>/bitbucket-pipelines.yml; verify the file contains valid YAML
code.....: 2
file.....: /home/adr1an/.config/composer/vendor/ktomk/pipelines/src/File/File.php
line.....: 53
backtrace:
#0 /home/adr1an/.config/composer/vendor/ktomk/pipelines/src/Utility/App.php(135): Ktomk\Pipelines\File\File::createFromFile()
#1 /home/adr1an/.config/composer/vendor/ktomk/pipelines/src/Utility/ExceptionHandler.php(50): Ktomk\Pipelines\Utility\App->run()
#2 /home/adr1an/.config/composer/vendor/ktomk/pipelines/src/Utility/ExceptionHandler.php(65): Ktomk\Pipelines\Utility\ExceptionHandler->handle()
#3 /home/adr1an/.config/composer/vendor/ktomk/pipelines/src/Utility/App.php(90): Ktomk\Pipelines\Utility\ExceptionHandler->handleStatus()
#4 /home/adr1an/.config/composer/vendor/ktomk/pipelines/bin/pipelines(37): Ktomk\Pipelines\Utility\App->main()
#5 /home/adr1an/.config/composer/vendor/bin/pipelines(112): include('/home/adr1an/.c...')
#6 {main}
--------

EDIT

It turns out that the parser does not like anchors and this is the root of the problem.

@ktomk
Copy link
Owner

ktomk commented Apr 11, 2022

@acederberg there can be subtle differences with YAML parsing across platforms. IIRC if you compare w/ python, consider to add the PHP YAML extension if possible. There is a fallback mechanism and if one parser fails, the other is tried. With PHP ext-yaml, there is a second chance.

For the concrete problem if you can add a (reduced is fine, too) bitbucket-pipelines.yml that triggers this issue would be good so I can tell more.

@acederberg
Copy link
Author

acederberg commented Apr 13, 2022

@ktomk Thanks! I (think I) figured it out. I'm using some YAML anchors and that is causing the issue. I rendered my YAML with python and dumped it into a new pipeline YAML and it works ( it uses all anchors besides the <<: anchor when rendered, so I think that it is the problem ). Would it be possible to add this? IDK anything about PHP hardly, so it's hard to decide where to start.

For example

image : python:3.10

options : { docker : true }

definitions :
  services :
    mongodb :
      environment :
        MONGO_INITDB_ROOT_USERNAME : adrian
        MONGO_INITDB_ROOT_PASSWORD : somepassword
        MONGO_INITDB_DATABASE : tests
      image : mongo
  steps :
    - step : &create_config
        name : "Create '.env'."
        script :
          - export ENV_PATH="$PWD/.env"
          - touch $ENV_PATH
          - echo "mongodb_host='brain_rtus_db'" >> $ENV_PATH
          - echo "mongodb_port=27017" >> $ENV_PATH
          - echo "mongodb_user='adrian'" >> $ENV_PATH
          - echo "mongodb_password='somepassword'" >> $ENV_PATH
          - echo "mongodb_use_atlas='False'" >> $ENV_PATH
          - echo "uvicorn_port=8001" >> $ENV_PATH
          - echo "uvicorn_host='localhost'" >> $ENV_PATH
          - echo "uvicorn_reload='True'" >> $ENV_PATH
          - realpath $ENV_PATH
        artifacts :
          - .env

pipelines :
  default :
    - step :
       <<: *create_config

is invalid. But when I render it with python to get different anchors :

definitions:
  services:
    mongodb:
      environment:
        MONGO_INITDB_DATABASE: tests
        MONGO_INITDB_ROOT_PASSWORD: somepassword
        MONGO_INITDB_ROOT_USERNAME: adrian
      image: mongo
  steps:
  - step:
      artifacts: &id001
      - .env
      name: Create '.env'.
      script: &id002
      - export ENV_PATH="$PWD/.env"
      - touch $ENV_PATH
      - echo "mongodb_host='brain_rtus_db'" >> $ENV_PATH
      - echo "mongodb_port=27017" >> $ENV_PATH
      - echo "mongodb_user='adrian'" >> $ENV_PATH
      - echo "mongodb_password='somepassword'" >> $ENV_PATH
      - echo "mongodb_use_atlas='False'" >> $ENV_PATH
      - echo "uvicorn_port=8001" >> $ENV_PATH
      - echo "uvicorn_host='localhost'" >> $ENV_PATH
      - echo "uvicorn_reload='True'" >> $ENV_PATH
      - realpath $ENV_PATH
image: python:3.10
options:
  docker: true
pipelines :
  default :
  - step:
      artifacts: *id001
      name: Create '.env'.
      script: *id002

it is valid.

@acederberg acederberg changed the title My YAML file cannot be parsed despite passing validation on bitbucket Anchors Do Not Work - Raises Invalid YAML Apr 13, 2022
@acederberg acederberg changed the title Anchors Do Not Work - Raises Invalid YAML (Some) Anchors Do Not Work - Raises Invalid YAML Apr 13, 2022
@ktomk
Copy link
Owner

ktomk commented Apr 14, 2022

thanks for looking further into it @acederberg

I (think I) figured it out. I'm using some YAML anchors ...

Okay, I can see that. And you got me for a moment because this YAML merge keys can be problematic (in general). I've written a summary of these in YAML Anchor, Aliases and Merge Keys, see especially the section Caveat: Support of Merge Keys.

However, and thanks to your example YAML, I had no problems to use it with pipelines (pipelines: version 0.0.62+6-g3dc9e6a+dirty w/ php 7.4.28 (libyaml: 2.1.0)) and it is not because I have the PHP yaml extension as I have disabled that YAML parser internally for this test.

Toggle me!
$ bin/pipelines --file test/data/yml/merge-key.yml --step-script --debug
# this /bin/sh script is generated from a pipeline script
set -e
printf '\n'
printf '\035+ %s\n' 'export ENV_PATH="$PWD/.env"'
export ENV_PATH="$PWD/.env"
printf '\n'
printf '\035+ %s\n' 'touch $ENV_PATH'
touch $ENV_PATH
printf '\n'
printf '\035+ %s\n' 'echo "mongodb_host='\'brain_rtus_db\''" >> $ENV_PATH'
echo "mongodb_host='brain_rtus_db'" >> $ENV_PATH
printf '\n'
printf '\035+ %s\n' 'echo "mongodb_port=27017" >> $ENV_PATH'
echo "mongodb_port=27017" >> $ENV_PATH
printf '\n'
printf '\035+ %s\n' 'echo "mongodb_user='\'adrian\''" >> $ENV_PATH'
echo "mongodb_user='adrian'" >> $ENV_PATH
printf '\n'
printf '\035+ %s\n' 'echo "mongodb_password='\'somepassword\''" >> $ENV_PATH'
echo "mongodb_password='somepassword'" >> $ENV_PATH
printf '\n'
printf '\035+ %s\n' 'echo "mongodb_use_atlas='\'False\''" >> $ENV_PATH'
echo "mongodb_use_atlas='False'" >> $ENV_PATH
printf '\n'
printf '\035+ %s\n' 'echo "uvicorn_port=8001" >> $ENV_PATH'
echo "uvicorn_port=8001" >> $ENV_PATH
printf '\n'
printf '\035+ %s\n' 'echo "uvicorn_host='\'localhost\''" >> $ENV_PATH'
echo "uvicorn_host='localhost'" >> $ENV_PATH
printf '\n'
printf '\035+ %s\n' 'echo "uvicorn_reload='\'True\''" >> $ENV_PATH'
echo "uvicorn_reload='True'" >> $ENV_PATH
printf '\n'
printf '\035+ %s\n' 'realpath $ENV_PATH'
realpath $ENV_PATH
pipelines: version 0.0.62+6-g3dc9e6a+dirty w/ php 7.4.28 (libyaml: 2.1.0)
--------
class....: Ktomk\Pipelines\Utility\StatusException
message..:
code.....: 0
file.....: .../pipelines/src/Utility/StatusException.php
line.....: 41
backtrace:
#0 .../src/Utility/StepScriptOption.php(111): Ktomk\Pipelines\Utility\StatusException::ok()
#1 .../src/Utility/App.php(141): Ktomk\Pipelines\Utility\StepScriptOption->run()
#2 .../src/Utility/ExceptionHandler.php(50): Ktomk\Pipelines\Utility\App->run()
#3 .../src/Utility/ExceptionHandler.php(65): Ktomk\Pipelines\Utility\ExceptionHandler->handle()
#4 .../src/Utility/App.php(90): Ktomk\Pipelines\Utility\ExceptionHandler->handleStatus()
#5 .../bin/pipelines(38): Ktomk\Pipelines\Utility\App->main()
#6 {main}
--------

So merge keys might be related but also are supported (in pipelines(1)). This is interesting, as it does not work on your end.

Perhaps there is something different in the file triggering the issue?

/E: Test changes here: 497d505
/E: An on this branch: https://github.com/ktomk/pipelines/tree/test-issue-14

@acederberg
Copy link
Author

Hi again @ktomk. Looking into it still and it appears that I am entirely wrong about anchors. I completely deanchored another yaml and it does not work, I am free to share this yaml. here it is :

definitions:
  services:
    mysql:
      image: mysql
      variables:
        MYSQL_DATABASE: mve_brain_sqlalchemy_tests
        MYSQL_PASSWORD: somepassword
        MYSQL_RANDOM_ROOT_PASSWORD: 'yes'
        MYSQL_USER: adrian
  steps:
  - step:
      artifacts:
      - .env.test
      - fetchers/.weather.env
      name: Create .env.test for testing since this file should never be commited.
      script:
      - export ENV_TEST_PATH=".env.test"
      - touch $ENV_TEST_PATH
      - echo 'mysql_database="mve_brain_sqlalchemy_tests"' >> $ENV_TEST_PATH
      - echo 'mysql_host=127.0.0.1' >> $ENV_TEST_PATH
      - echo 'mysql_port=3306' >> $ENV_TEST_PATH
      - echo 'mysql_username="adrian"' >> $ENV_TEST_PATH
      - echo 'mysql_password="somepassword"' >> $ENV_TEST_PATH
      - echo 'mysql_drivername="mysql+asyncmy"' >> $ENV_TEST_PATH
      - echo 'uvicorn_host="0.0.0.0"' >> $ENV_TEST_PATH
      - echo 'uvicorn_port=8000' >> $ENV_TEST_PATH
      - export ENV_TEST_FETCHERS=./api/fetchers/.fetchers.env
      - touch $ENV_TEST_FETCHERS
      - echo "weather_key=$WEATHER_KEY" >> $ENV_TEST_FETCHERS
image: python:3.10
options:
  docker: true
pipelines:
  custom:
    build_pipeline_service:
    - step:
        artifacts:
        - .env.test
        - fetchers/.weather.env
        name: Create .env.test for testing since this file should never be commited.
        script:
        - export ENV_TEST_PATH=".env.test"
        - touch $ENV_TEST_PATH
        - echo 'mysql_database="mve_brain_sqlalchemy_tests"' >> $ENV_TEST_PATH
        - echo 'mysql_host=127.0.0.1' >> $ENV_TEST_PATH
        - echo 'mysql_port=3306' >> $ENV_TEST_PATH
        - echo 'mysql_username="adrian"' >> $ENV_TEST_PATH
        - echo 'mysql_password="somepassword"' >> $ENV_TEST_PATH
        - echo 'mysql_drivername="mysql+asyncmy"' >> $ENV_TEST_PATH
        - echo 'uvicorn_host="0.0.0.0"' >> $ENV_TEST_PATH
        - echo 'uvicorn_port=8000' >> $ENV_TEST_PATH
        - export ENV_TEST_FETCHERS=./api/fetchers/.fetchers.env
        - touch $ENV_TEST_FETCHERS
        - echo "weather_key=$WEATHER_KEY" >> $ENV_TEST_FETCHERS
    - step:
        name: "Building an api image to be used in other pipelines using development\
          \ container multistage build. \nThis includes the .env file constructed\
          \ here.\n"
        script:
        - cp .env.test .env
        - export IMAGE_NAME="$DOCKERHUB_USERNAME/$DOCKERHUB_BRAIN_REPOSITORY"
        - export IMAGE_PIPELINE_SERVICE="$IMAGE_NAME:api_pipeline_service_$BITBUCKET_COMMIT"
        - docker login --username $DOCKERHUB_USERNAME --password $DOCKERHUB_PASSWORD
        - docker build -t "$IMAGE_PIPELINE_SERVICE" -f "Dockerfile" .
        - docker run --name test2 --detach "$IMAGE_PIPELINE_SERVICE"
        - sleep 15
        - docker stop test2
        - docker push $IMAGE_NAME
  default:
  - step:
      caches:
      - pip
      name: Install all dependencies.
      script:
      - pip install -r requirements.txt
      - pip install -r requirements.dev.txt
  - step:
      artifacts:
      - .env.test
      - fetchers/.weather.env
      name: Create .env.test for testing since this file should never be commited.
      script:
      - export ENV_TEST_PATH=".env.test"
      - touch $ENV_TEST_PATH
      - echo 'mysql_database="mve_brain_sqlalchemy_tests"' >> $ENV_TEST_PATH
      - echo 'mysql_host=127.0.0.1' >> $ENV_TEST_PATH
      - echo 'mysql_port=3306' >> $ENV_TEST_PATH
      - echo 'mysql_username="adrian"' >> $ENV_TEST_PATH
      - echo 'mysql_password="somepassword"' >> $ENV_TEST_PATH
      - echo 'mysql_drivername="mysql+asyncmy"' >> $ENV_TEST_PATH
      - echo 'uvicorn_host="0.0.0.0"' >> $ENV_TEST_PATH
      - echo 'uvicorn_port=8000' >> $ENV_TEST_PATH
      - export ENV_TEST_FETCHERS=./api/fetchers/.fetchers.env
      - touch $ENV_TEST_FETCHERS
      - echo "weather_key=$WEATHER_KEY" >> $ENV_TEST_FETCHERS
  - step:
      caches:
      - pip
      name: Test
      script:
      - pip install -r requirements.txt
      - pip install -r requirements.dev.txt
      - python -m pytest .
      services:
      - mysql
  - step:
      caches:
      - pip
      name: Building prod and fetchers docker images (without an enironment files)
      script:
      - export IMAGE_NAME="$DOCKERHUB_USERNAME/$DOCKERHUB_BRAIN_REPOSITORY"
      - export IMAGE_API="$IMAGE_NAME:api_$BITBUCKET_COMMIT"
      - export IMAGE_FETCHERS="$IMAGE_NAME:fetchers_$BITBUCKET_COMMIT"
      - docker login --username $DOCKERHUB_USERNAME --password $DOCKERHUB_PASSWORD
      - docker build -t "$IMAGE_API" -f "Dockerfile.prod" --target "prod" .
      - docker build -t "$IMAGE_FETCHERS" -f "Dockerfile.prod" --target "fetcher_runner"
        .
      - docker run --name test1 --detach "$IMAGE_API"
      - sleep 30
      - docker stop test1
      - docker push $IMAGE_NAME

@ktomk
Copy link
Owner

ktomk commented Apr 14, 2022

With the Sf2Yaml it works on my end.

With the LibYaml it does not and I could find two "culprits":

  1. The double quoted string (line 56, name) with the line continuations looks not supported
  2. The spurious dot (line 117) looks not supported

If I remove both, it works with the libyaml parser, too.

Wondering a bit why it's not working on your end as IIRC the Sf2Yaml parser is taken on your end as you don't have LibYaml. @acederberg

@acederberg
Copy link
Author

acederberg commented Apr 14, 2022

Here's the verbose output prior to those changes mentioned above :

❯ pipelines --file bitbucket-pipelines-deanchored.yml --debug --verbose
info: project directory is '/mnt/c/MVE/mve-brain-services/mve-brain-api'
info: pipelines file is '/mnt/c/MVE/mve-brain-services/mve-brain-api/bitbucket-pipelines-deanchored.yml'
pipelines: file parse error: YAML error: /mnt/c/MVE/mve-brain-services/mve-brain-api/bitbucket-pipelines-deanchored.yml; verify the file contains valid YAML
pipelines: version 0.0.62-composer w/ php 7.4.3 (libyaml: n/a)
--------
class....: Ktomk\Pipelines\File\ParseException
message..: file parse error: YAML error: /mnt/c/MVE/mve-brain-services/mve-brain-api/bitbucket-pipelines-deanchored.yml; verify the file contains valid YAML
code.....: 2
file.....: /home/adr1an/.config/composer/vendor/ktomk/pipelines/src/File/File.php
line.....: 53
backtrace:
#0 /home/adr1an/.config/composer/vendor/ktomk/pipelines/src/Utility/App.php(135): Ktomk\Pipelines\File\File::createFromFile()
#1 /home/adr1an/.config/composer/vendor/ktomk/pipelines/src/Utility/ExceptionHandler.php(50): Ktomk\Pipelines\Utility\App->run()
#2 /home/adr1an/.config/composer/vendor/ktomk/pipelines/src/Utility/ExceptionHandler.php(65): Ktomk\Pipelines\Utility\ExceptionHandler->handle()
#3 /home/adr1an/.config/composer/vendor/ktomk/pipelines/src/Utility/App.php(90): Ktomk\Pipelines\Utility\ExceptionHandler->handleStatus()
#4 /home/adr1an/.config/composer/vendor/ktomk/pipelines/bin/pipelines(37): Ktomk\Pipelines\Utility\App->main()
#5 /home/adr1an/.config/composer/vendor/bin/pipelines(112): include('/home/adr1an/.c...')
#6 {main}
--------

I got it to run after fixing those, so it looks like some whacky stuff is happening with pyyaml causing it to dump the YAML with those errors. I am still ( and now to a greater degree than before ) confused as there is still something in my other yaml that will make it fail after it's rendered. IDK what I am doing wrong elsewhere, but it must be an error in my yaml and not the parser itself.

Is there a way to get better feedback on the YAML errors from pipelines? e.g. "bad syntax on line n column k"

@ktomk
Copy link
Owner

ktomk commented Apr 14, 2022

Well the YAML parser in pipelines most likely isn't golden. I had issues earlier and from where its coming from it was always with some fallback. I'll tweak the handling based on the feedback here probably. Thinking about putting the Sf2Yaml parser upfront and use the LibYaml one as fallback (currently the other way round, but what I learned this is good for a change).

Is there a way to get better feedback on the YAML errors from pipelines? e.g. "bad syntax on line n column k"

Yes, that would be good to at least have some pointers. I'll patch something in, it should provide more info at least in some cases. It's not entirely structural like line and column AFAIK.

@ktomk
Copy link
Owner

ktomk commented Apr 14, 2022

Okay, I made a mistake in testing as I now see:

Malformed inline YAML string ("Building an api image to be used in other pipelines using development) at line 56 (near "name: "Building an api image to be used in other pipelines using development").

This is from the Sf2Yaml parser. The LibYaml parser is fine. Looks like I mixed it up. That should explain why I was puzzled earlier. I'll put error messages in. It will be a bit text-walling but better than current.

ktomk added a commit that referenced this issue Apr 14, 2022
previously when parsing a pipelines YAML file that could not be parsed, for
example b/c of unsupported syntax in the YAML parser, pipelines did only
notify which file and that it was invalid for pipelines.

addition is to capture error messages so that when YAML parsing fails, this
error message is extended.

report: #14
@ktomk
Copy link
Owner

ktomk commented Apr 14, 2022

Version 0.0.63 has been released showing more detailed error messages on YAML file parse errors, Link to full change-log..

@acederberg
Copy link
Author

@ktomk Thank you! For the reference of anybody who needs help in the future who is unfamiliar with php package managers, update by editing the version number in ~/.config/composer/composer.json and then run composer global update ktomk/pipelines.

@ktomk
Copy link
Owner

ktomk commented Apr 15, 2022

@acederberg: Yes, if you install with composer global, you can update within the stable channel even without editing the composer.json by hand:

composer global require ktomk/pipelines:^0.0

Takes the latest 0.0.x version (as require also updates).

It is using require here so it works with however you have set it up originally.

As it also works from the source version, it should be possible as well to install non-tagged revisions with composer:

$ composer global require ktomk/pipelines:dev-test-issue-14
...
$ pipelines --version
pipelines version dev-test-issue-14-composer

How did you learn about installing this utility?

Just curious and I may think about improving the docs about it.

@ktomk ktomk added the accessibility easy access and use of pipeline features label Apr 15, 2022
@acederberg
Copy link
Author

@ktomk Hi Just saw your last comment. I found it by searching 'run bitbucket pipelines locally' out of frustration with the workflow of debugging pipelines. I found your kb which was the best resource I could find on the topic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
accessibility easy access and use of pipeline features
Projects
None yet
Development

No branches or pull requests

2 participants