pax_global_header00006660000000000000000000000064151525446110014516gustar00rootroot0000000000000052 comment=432a00368261e01c2827071d67c477bfe4a55664 buildstream-plugins-2.7.0/000077500000000000000000000000001515254461100155165ustar00rootroot00000000000000buildstream-plugins-2.7.0/.asf.yaml000066400000000000000000000015531515254461100172350ustar00rootroot00000000000000 # # Avoid diluting the dev mailing list with meta stuff, # redirect it all to the commit list. # notifications: commits: commits@buildstream.apache.org issues: commits@buildstream.apache.org pullrequests: commits@buildstream.apache.org # # Configure github # github: description: "BuildStream plugins" homepage: https://buildstream.build/ # Main features features: # Enable wiki for documentation wiki: true # Enable issue management issues: true # Enable projects for project management boards projects: true # Buttons enabled_merge_buttons: # Disable squash button: squash: false # enable merge button: merge: true # disable rebase button: rebase: false # Close branches when pull requests are merged del_branch_on_merge: true # Enable pages publishing ghp_branch: gh-pages ghp_path: /docs buildstream-plugins-2.7.0/.github/000077500000000000000000000000001515254461100170565ustar00rootroot00000000000000buildstream-plugins-2.7.0/.github/CODEOWNERS000066400000000000000000000003641515254461100204540ustar00rootroot00000000000000# Each line is a file pattern followed by one or more owners. # These owners will be the default owners for everything in # the repo, unless a later match takes precedence. # * @gtristan @juergbi @BenjaminSchubert @cs-shadow @abderrahim buildstream-plugins-2.7.0/.github/common.env000066400000000000000000000003201515254461100210530ustar00rootroot00000000000000# Shared common variables CI_IMAGE_VERSION=master-2310077904 CI_TOXENV_ALL=py310,py311,py312,py313,py314 CI_TOXENV_MASTER=py310-bst-master,py311-bst-master,py312-bst-master,py313-bst-master,py314-bst-master buildstream-plugins-2.7.0/.github/compose/000077500000000000000000000000001515254461100205235ustar00rootroot00000000000000buildstream-plugins-2.7.0/.github/compose/ci.docker-compose.yml000066400000000000000000000036171515254461100245610ustar00rootroot00000000000000version: '3.4' x-tests-template: &tests-template image: registry.gitlab.com/buildstream/buildstream-docker-images/testsuite-fedora:42-${CI_IMAGE_VERSION:-latest} command: tox -vvvvv -- --color=yes --integration environment: TOXENV: ${CI_TOXENV_ALL} # Enable privileges to run the sandbox # privileged: true devices: - /dev/fuse:/dev/fuse # Mount the local directory and set the working directory # to run the tests from. # volumes: - ../..:/home/testuser/buildstream working_dir: /home/testuser/buildstream services: debian-12: <<: *tests-template image: registry.gitlab.com/buildstream/buildstream-docker-images/testsuite-debian:12-${CI_IMAGE_VERSION:-latest} debian-13: <<: *tests-template image: registry.gitlab.com/buildstream/buildstream-docker-images/testsuite-debian:13-${CI_IMAGE_VERSION:-latest} fedora-42: <<: *tests-template image: registry.gitlab.com/buildstream/buildstream-docker-images/testsuite-fedora:42-${CI_IMAGE_VERSION:-latest} fedora-43: <<: *tests-template image: registry.gitlab.com/buildstream/buildstream-docker-images/testsuite-fedora:43-${CI_IMAGE_VERSION:-latest} ubuntu-22.04: <<: *tests-template image: registry.gitlab.com/buildstream/buildstream-docker-images/testsuite-ubuntu:22.04-${CI_IMAGE_VERSION:-latest} # Ensure that tests also pass in the absence of a sandboxing tool fedora-missing-deps: <<: *tests-template image: registry.gitlab.com/buildstream/buildstream-docker-images/testsuite-fedora:minimal-${CI_IMAGE_VERSION:-latest} # Test against the master version of BuildStream bst-master: <<: *tests-template environment: TOXENV: ${CI_TOXENV_MASTER} docs: <<: *tests-template command: tox -e docs lint: <<: *tests-template command: tox -e lint,format-check mypy: <<: *tests-template command: tox -e mypy buildstream-plugins-2.7.0/.github/run-ci.sh000077500000000000000000000033051515254461100206130ustar00rootroot00000000000000#!/bin/bash topdir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" function usage () { echo "Usage: " echo " run-ci.sh [OPTIONS] [TEST NAME [TEST NAME...]]" echo echo "Runs the CI tests locally using docker" echo echo "The test names are based on the names of tests in the CI yaml files" echo echo "If no test names are specified, all tests will be run" echo echo "Options:" echo echo " -h --help Display this help message and exit" echo " " exit 1; } arg_service=false while : ; do case "$1" in -h|--help) usage; shift ;; -s|--service) arg_service=true shift ;; *) break ;; esac done test_names="${@}" # We need to give ownership to the docker image user `testuser`, # chances are high that this will be the same UID as the primary # user on this host # user_uid="$(id -u)" user_gid="$(id -g)" if [ "${user_uid}" -ne "1000" ] || [ "${user_gid}" -ne "1000" ]; then sudo chown -R 1000:1000 "${topdir}/.." fi # runTest() # # $1 = test name # function runTest() { test_name=$1 # Run docker-compose from it's directory, because it will use # relative paths cd "${topdir}/compose" docker compose \ --env-file ${topdir}/common.env \ --file ${topdir}/compose/ci.docker-compose.yml \ run "${test_name}" return $? } if [ -z "${test_names}" ]; then for test_name in "mypy debian-12 debian-13 fedora-42 fedora-43 fedora-missing-deps ubuntu-22.04"; do if ! runTest "${test_name}"; then echo "Tests failed" exit 1 fi done else for test_name in "${test_names}"; do if ! runTest "${test_name}"; then echo "Tests failed" exit 1 fi done fi buildstream-plugins-2.7.0/.github/workflows/000077500000000000000000000000001515254461100211135ustar00rootroot00000000000000buildstream-plugins-2.7.0/.github/workflows/ci.yml000066400000000000000000000046121515254461100222340ustar00rootroot00000000000000name: PR Checks # Pre-merge CI to run on push and pull_request events, even if this seems # redundant, we avoid concurrency with the below configuration. # on: pull_request: workflow_dispatch: # Use the concurrency feature to ensure we don't run redundant workflows # concurrency: group: ${{ github.repository }}-${{ github.ref }}-${{ github.workflow }} cancel-in-progress: true # Left to-do: # - coverage # - publishing docs to gh-pages # - persistent artifact cache # - overnight jobs # - wsl tasks (TODO: Check if GitHub's Windows runners allow WSL) # # New opportunities: # - run tests on mac (GitHub provides MacOS runners) # - standardize WSL tasks by using GitHub-provided runners jobs: tests: runs-on: ubuntu-24.04 continue-on-error: ${{ matrix.allow-failure || false }} strategy: fail-fast: false matrix: # The names here should map to a valid service defined in # "../compose/ci.docker-compose.yml" test-name: - debian-12 - debian-13 - fedora-42 - fedora-43 - fedora-missing-deps - ubuntu-22.04 - lint - mypy include: - test-name: bst-master allow-failure: true steps: - name: Disable AppArmor restriction for bubblewrap run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 - name: Check out repository uses: actions/checkout@v2 # BuildStream requires tags to be able to find its version. with: fetch-depth: 0 - name: Run tests with Docker Compose run: | ${GITHUB_WORKSPACE}/.github/run-ci.sh ${{ matrix.test-name }} docs: runs-on: ubuntu-24.04 steps: - name: Disable AppArmor restriction for bubblewrap run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 - name: Check out repository uses: actions/checkout@v2 # BuildStream requires tags to be able to find its version. with: fetch-depth: 0 - name: Give `testuser` ownership of the source directory run: sudo chown -R 1000:1000 ${GITHUB_WORKSPACE} - name: Build documentation using Docker Compose run: | ${GITHUB_WORKSPACE}/.github/run-ci.sh docs - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: docs path: doc/build/html buildstream-plugins-2.7.0/.github/workflows/merge.yml000066400000000000000000000051651515254461100227440ustar00rootroot00000000000000name: Merge actions on: push: branches: - master jobs: build: name: Build documentation runs-on: ubuntu-24.04 steps: - name: Disable AppArmor restriction for bubblewrap run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 - name: Checkout code uses: actions/checkout@v2 # BuildStream requires tags to be able to find its version. with: fetch-depth: 0 - name: Give `testuser` ownership of the source directory run: sudo chown -R 1000:1000 ${GITHUB_WORKSPACE} - name: Build documentation using Docker Compose run: | ${GITHUB_WORKSPACE}/.github/run-ci.sh docs # Restore permissions to the current user sudo chown -R ${USER} ${GITHUB_WORKSPACE} # Include a tarball in the published docs, allowing for # easy re-publishing of master docs on docs.buildstream.build tar -C doc/build/html -zcf docs.tgz . - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: docs path: | doc/build/html docs.tgz publish: needs: build runs-on: ubuntu-24.04 steps: - name: Disable AppArmor restriction for bubblewrap run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 - name: Download artifact uses: actions/download-artifact@v4 with: name: docs path: docs - name: Checkout code uses: actions/checkout@v2 with: ref: gh-pages path: pages fetch-depth: 0 - name: Update repo run: | # First reset the branch state to the initial commit, this ensures that # we do not pollute the repository with the history of every docs package # we've ever published (history of docs packages for major releases is # also stored as GitHub release assets) # cd pages/ git reset --hard GH_PAGES_FIRST_COMMIT mkdir docs # Copy the docs asset over and overwrite the orphan gh-pages branch, ensure # that we disable GitHub's jekyll by creating the .nojekyll file, otherwise # it will interfere with the rendering of the site. # cp -a ../docs/doc/build/html/* docs cp -a ../docs/docs.tgz docs touch .nojekyll touch docs/.nojekyll git add . git config --local user.email "merge-ci@ponyland" git config --local user.name "Github Actions Nightly Job" git commit -m "Update repo for docs build $GITHUB_RUN_NUMBER" git push --force "https://$GITHUB_ACTOR:$GITHUB_TOKEN@github.com/$GITHUB_REPOSITORY.git" gh-pages buildstream-plugins-2.7.0/.pylintrc000066400000000000000000000417301515254461100173700ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # [MASTER] # A comma-separated list of package or module names from where C extensions may # be loaded. Extensions are loading into the active Python interpreter and may # run arbitrary code extension-pkg-whitelist= buildstream.node, buildstream._loader.loadelement, buildstream._loader.types, buildstream._types, buildstream._utils, buildstream._variables, buildstream._yaml, ujson # Add files or directories to the blacklist. They should be base names, not # paths. ignore=CVS,doc # Add files or directories matching the regex patterns to the blacklist. The # regex matches against base names, not paths. ignore-patterns=.*_pb2.py,.*_pb2_grpc.py # Python code to execute, usually for sys.path manipulation such as # pygtk.require(). #init-hook= # Use multiple processes to speed up Pylint. jobs=1 # List of plugins (as comma separated values of python modules names) to load, # usually to register additional checkers. load-plugins= # Pickle collected data for later comparisons. persistent=yes # Specify a configuration file. #rcfile= # When enabled, pylint would attempt to guess common misconfiguration and emit # user-friendly hints instead of false-positive error messages suggestion-mode=yes # Allow loading of arbitrary C extensions. Extensions are imported into the # active Python interpreter and may run arbitrary code. unsafe-load-any-extension=no [MESSAGES CONTROL] # Only show warnings with the listed confidence levels. Leave empty to show # all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED confidence= # Disable the message, report, category or checker with the given id(s). You # can either give multiple identifiers separated by comma (,) or put this # option multiple times (only on the command line, not in the configuration # file where it should appear only once).You can also use "--disable=all" to # disable everything first and then reenable specific checks. For example, if # you want to run only the similarities checker, you can use "--disable=all # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use"--disable=all --enable=classes # --disable=W" # We have three groups of disabled messages: # # 1) Messages that are of no use to us # This is either because we don't follow the convention # (missing-docstring and protected-access come to mind), or because # it's not very useful in CI (too-many-arguments, for example) # # 2) Messages that we would like to enable at some point # We introduced linting quite late into the project, so there are # some issues that just grew out of control. Resolving these would # be nice, but too much work atm. # # 3) Messages related to code formatting # Since we use Black to format code automatically, there's no need for # pylint to also check for those things. # disable= ##################################### # Messages that are of no use to us # ##################################### , consider-using-f-string, fixme, missing-docstring, no-else-return, protected-access, too-few-public-methods, too-many-arguments, too-many-boolean-expressions, too-many-branches, too-many-instance-attributes, too-many-lines, too-many-locals, too-many-nested-blocks, too-many-positional-arguments, too-many-public-methods, too-many-statements, too-many-return-statements, too-many-ancestors, # BuildStream plugins define instance variables in configure() attribute-defined-outside-init, ####################################################### # Messages that we would like to enable at some point # ####################################################### # We have many circular imports that need breaking import-outside-toplevel, duplicate-code, # Some invalid names are alright, we should configure pylint # to accept them, and curb the others invalid-name, unused-argument, # This is good to get context on exceptions, we should enable that # at some point raise-missing-from, # We can probably enable this soon, it is a bit experimental # for the moment and current releases of pylint (August 2021) raise # a lot of false positives. unused-private-member, ################################################## # Formatting-related messages, enforced by Black # ################################################## line-too-long, superfluous-parens, # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option # multiple time (only on the command line, not in the configuration file where # it should appear only once). See also the "--disable" option for examples. enable=c-extension-no-member [REPORTS] # Python expression which should return a note less than 10 (10 is the highest # note). You have access to the variables errors warning, statement which # respectively contain the number of errors / warnings messages and the total # number of statements analyzed. This is used by the global evaluation report # (RP0004). evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) # Template used to display messages. This is a python new-style format string # used to format the message information. See doc for all details #msg-template= # Set the output format. Available formats are text, parseable, colorized, json # and msvs (visual studio).You can also give a reporter class, eg # mypackage.mymodule.MyReporterClass. output-format=colorized # Tells whether to display a full report or only the messages reports=no # Activate the evaluation score. score=yes [REFACTORING] # Maximum number of nested blocks for function / method body max-nested-blocks=5 # Complete name of functions that never returns. When checking for # inconsistent-return-statements if a never returning function is called then # it will be considered as an explicit return statement and no message will be # printed. never-returning-functions=optparse.Values,sys.exit [TYPECHECK] # List of decorators that produce context managers, such as # contextlib.contextmanager. Add to this list to register other decorators that # produce valid context managers. contextmanager-decorators=contextlib.contextmanager # List of members which are set dynamically and missed by pylint inference # system, and so shouldn't trigger E1101 when accessed. Python regular # expressions are accepted. generated-members=__enter__ # Tells whether missing members accessed in mixin class should be ignored. A # mixin class is detected if its name ends with "mixin" (case insensitive). ignore-mixin-members=yes # This flag controls whether pylint should warn about no-member and similar # checks whenever an opaque object is returned when inferring. The inference # can return multiple potential results while evaluating a Python object, but # some branches might not be evaluated, which results in partial inference. In # that case, it might be useful to still emit no-member and other checks for # the rest of the inferred objects. ignore-on-opaque-inference=yes # List of class names for which member attributes should not be checked (useful # for classes with dynamically set attributes). This supports the use of # qualified names. ignored-classes=optparse.Values,thread._local,_thread._local,contextlib.closing,gi.repository.GLib.GError,pathlib.PurePath # List of module names for which member attributes should not be checked # (useful for modules/projects where namespaces are manipulated during runtime # and thus existing member attributes cannot be deduced by static analysis. It # supports qualified module names, as well as Unix pattern matching. ignored-modules=pkg_resources,gi.repository,grpc,buildstream._protos.* # Show a hint with possible names when a member name was not found. The aspect # of finding the hint is based on edit distance. missing-member-hint=yes # The minimum edit distance a name should have in order to be considered a # similar match for a missing member name. missing-member-hint-distance=1 # The total number of similar names that should be taken in consideration when # showing a hint for a missing member. missing-member-max-choices=1 [BASIC] # Naming style matching correct argument names argument-naming-style=snake_case # Regular expression matching correct argument names. Overrides argument- # naming-style #argument-rgx= # Naming style matching correct attribute names attr-naming-style=snake_case # Regular expression matching correct attribute names. Overrides attr-naming- # style #attr-rgx= # Bad variable names which should always be refused, separated by a comma bad-names=foo, bar, baz, toto, tutu, tata # Naming style matching correct class attribute names class-attribute-naming-style=any # Regular expression matching correct class attribute names. Overrides class- # attribute-naming-style #class-attribute-rgx= # Naming style matching correct class names class-naming-style=PascalCase # Regular expression matching correct class names. Overrides class-naming-style #class-rgx= # Naming style matching correct constant names const-naming-style=UPPER_CASE # Regular expression matching correct constant names. Overrides const-naming- # style #const-rgx= # Minimum line length for functions/classes that require docstrings, shorter # ones are exempt. docstring-min-length=-1 # Naming style matching correct function names function-naming-style=snake_case # Regular expression matching correct function names. Overrides function- # naming-style #function-rgx= # Good variable names which should always be accepted, separated by a comma good-names=i,j,k,ex,Run,_,e,f # Include a hint for the correct naming format with invalid-name include-naming-hint=no # Naming style matching correct inline iteration names inlinevar-naming-style=any # Regular expression matching correct inline iteration names. Overrides # inlinevar-naming-style #inlinevar-rgx= # Naming style matching correct method names method-naming-style=snake_case # Regular expression matching correct method names. Overrides method-naming- # style #method-rgx= # Naming style matching correct module names module-naming-style=snake_case # Regular expression matching correct module names. Overrides module-naming- # style #module-rgx= # Colon-delimited sets of names that determine each other's naming style when # the name regexes allow several styles. name-group= # Regular expression which should only match function or class names that do # not require a docstring. no-docstring-rgx=^_ # List of decorators that produce properties, such as abc.abstractproperty. Add # to this list to register other decorators that produce valid properties. property-classes=abc.abstractproperty # Naming style matching correct variable names variable-naming-style=snake_case # Regular expression matching correct variable names. Overrides variable- # naming-style #variable-rgx= [VARIABLES] # List of additional names supposed to be defined in builtins. Remember that # you should avoid to define new builtins when possible. additional-builtins= # Tells whether unused global variables should be treated as a violation. allow-global-unused-variables=yes # List of strings which can identify a callback function by name. A callback # name must start or end with one of those strings. callbacks=cb_, _cb # A regular expression matching the name of dummy variables (i.e. expectedly # not used). dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ # Argument names that match this expression will be ignored. Default to name # with leading underscore ignored-argument-names=_.*|^ignored_|^unused_ # Tells whether we should check for unused import in __init__ files. init-import=no # List of qualified module names which can have objects that can redefine # builtins. redefining-builtins-modules=six.moves,past.builtins,future.builtins [LOGGING] # Logging modules to check that the string format arguments are in logging # function parameter format logging-modules=logging [SPELLING] # Limits count of emitted suggestions for spelling mistakes max-spelling-suggestions=4 # Spelling dictionary name. Available dictionaries: none. To make it working # install python-enchant package. spelling-dict= # List of comma separated words that should not be checked. spelling-ignore-words= # A path to a file that contains private dictionary; one word per line. spelling-private-dict-file= # Tells whether to store unknown words to indicated private dictionary in # --spelling-private-dict-file option instead of raising a message. spelling-store-unknown-words=no [MISCELLANEOUS] # List of note tags to take in consideration, separated by a comma. notes=FIXME, XXX, TODO [SIMILARITIES] # Ignore comments when computing similarities. ignore-comments=yes # Ignore docstrings when computing similarities. ignore-docstrings=yes # Ignore imports when computing similarities. ignore-imports=no # Minimum lines number of a similarity. min-similarity-lines=4 [FORMAT] # Expected format of line ending, e.g. empty (any line ending), LF or CRLF. expected-line-ending-format= # Regexp for a line that is allowed to be longer than the limit. ignore-long-lines=^\s*(# )??$ # Number of spaces of indent required inside a hanging or continued line. indent-after-paren=4 # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 # tab). indent-string=' ' # Maximum number of characters on a single line. max-line-length=119 # Maximum number of lines in a module max-module-lines=1000 # Allow the body of a class to be on the same line as the declaration if body # contains single statement. single-line-class-stmt=no # Allow the body of an if to be on the same line as the test if there is no # else. single-line-if-stmt=no [IMPORTS] # Allow wildcard imports from modules that define __all__. allow-wildcard-with-all=no # Analyse import fallback blocks. This can be used to support both Python 2 and # 3 compatible code, which means that the block might have code that exists # only in one or another interpreter, leading to false positives when analysed. analyse-fallback-blocks=no # Deprecated modules which should not be used, separated by a comma deprecated-modules=optparse,tkinter.tix # Create a graph of external dependencies in the given file (report RP0402 must # not be disabled) ext-import-graph= # Create a graph of every (i.e. internal and external) dependencies in the # given file (report RP0402 must not be disabled) import-graph= # Create a graph of internal dependencies in the given file (report RP0402 must # not be disabled) int-import-graph= # Force import order to recognize a module as part of the standard # compatibility libraries. known-standard-library= # Force import order to recognize a module as part of a third party library. known-third-party=enchant [DESIGN] # Maximum number of arguments for function / method max-args=5 # Maximum number of attributes for a class (see R0902). max-attributes=7 # Maximum number of boolean expressions in a if statement max-bool-expr=5 # Maximum number of branch for function / method body max-branches=12 # Maximum number of locals for function / method body max-locals=15 # Maximum number of parents for a class (see R0901). max-parents=7 # Maximum number of public methods for a class (see R0904). max-public-methods=20 # Maximum number of return / yield for function / method body max-returns=6 # Maximum number of statements in function / method body max-statements=50 # Minimum number of public methods for a class (see R0903). min-public-methods=2 [CLASSES] # List of method names used to declare (i.e. assign) instance attributes. defining-attr-methods=__init__, __new__, setUp # List of member names, which should be excluded from the protected access # warning. exclude-protected=_asdict, _fields, _replace, _source, _make # List of valid names for the first argument in a class method. valid-classmethod-first-arg=cls # List of valid names for the first argument in a metaclass class method. valid-metaclass-classmethod-first-arg=mcs [EXCEPTIONS] # Exceptions that will emit a warning when being caught. Defaults to # "Exception" overgeneral-exceptions=Exception buildstream-plugins-2.7.0/COMMITTERS.rst000066400000000000000000000025061515254461100177410ustar00rootroot00000000000000.. _committers: Committers ========== Full commit access ------------------- List of people with full commit access, i.e. blanket commit access to the BuildStream plugins codebase. Note that this is not a full list of all contributors. +-----------------------------------+-----------------------------------+ | Full Name | GitHub User | +===================================+===================================+ | Tristan Van Berkom | gtristan | +-----------------------------------+-----------------------------------+ | Jürg Billeter | juergbi | +-----------------------------------+-----------------------------------+ | Chandan Singh | cs-shadow | +-----------------------------------+-----------------------------------+ | Benjamin Schubert | BenjaminSchubert | +-----------------------------------+-----------------------------------+ | Abderrahim Kitouni | abderrahim | +-----------------------------------+-----------------------------------+ | Sander Striker | sstriker | +-----------------------------------+-----------------------------------+ buildstream-plugins-2.7.0/LICENSE000066400000000000000000000261361515254461100165330ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. buildstream-plugins-2.7.0/MANIFEST.in000066400000000000000000000002301515254461100172470ustar00rootroot00000000000000global-include *.yaml include requirements/* include project.conf include COMMITTERS.rst include LICENSE include NOTICE include NEWS include README.rst buildstream-plugins-2.7.0/NEWS000066400000000000000000000071401515254461100162170ustar00rootroot00000000000000========================= buildstream-plugins 2.7.0 ========================= o cargo: Support source provenance (#98) o Fix multiple issues in the Docker source plugin (#96) o Support Python 3.14 and drop support for Python 3.9 (#94, #97) ========================= buildstream-plugins 2.5.0 ========================= o Drop support for python 3.8 and add support for python 3.13 (#84) o Implement Source.collect_source_info() or SourceFetcher.get_source_info() on all source plugins (#87) ========================= buildstream-plugins 2.4.0 ========================= o docker: Support storing ref in project.refs (#66) o simple_mirror: Added SourceMirror plugin supporting enhanced URL formatting (#80) o gitlab_lfs_mirror: Added SourceMirror intended for use with GitLab LFS repos (#80) o Enhanced testing and CI enhancements (#78) o Resurrecting CI (#81) ========================= buildstream-plugins 2.3.0 ========================= o pip: support Python 3.12 and future versions (#67) o cargo, docker: Check tar member paths (#69) o git: add tagger to annotated tags (fix for recent git versions) (#73) ========================= buildstream-plugins 2.2.0 ========================= o cargo: Fix mirroring (#55) o cargo: Support authentication using .netrc (#57) o docker: Fix authentication when using .netrc (#59) o docker: Improve flexibility for mirroring (#58) o cargo: Add support for source mirrors and bearer http authentication (#60, #64) ========================= buildstream-plugins 2.1.0 ========================= o Guard against malformed URIs in cargo crates (#52) ========================= buildstream-plugins 2.0.1 ========================= o docker source: Fix "architecture" and "os" configuration options o Updating some package metadata/links o Support Python 3.11 ========================= buildstream-plugins 2.0.0 ========================= No changes in 2.0.0 over 1.95.7 ========================== buildstream-plugins 1.95.7 ========================== o meson: Use `meson install` and rename JOBS environment variable (#41) o meson, cmake: Ensure verbosity (#42) ========================== buildstream-plugins 1.95.6 ========================== o Removed some deprecated variables from plugins (#34) o Avoid deprecation warnings in meson (#38) ========================== buildstream-plugins 1.95.5 ========================== o Updating copyright year in NOTICE file o Updating setup.py with updated author and documentaiton link o Updating generated documentation to use new author and copyright ========================== buildstream-plugins 1.95.4 ========================== o Fixing license headers in source files and ensuring that LICENSE / NOTICE are included in source distributions ========================== buildstream-plugins 1.95.3 ========================== o Revert the convenience build-dir attempt for autotools (#29) o Fix new linting errors o Manage release version in one place (#33) ========================== buildstream-plugins 1.95.1 ========================== o Fixing documentation generation o Updating pylint and correcting errors o Port cargo plugin to use tomli, this will remove the toml dependency as tomli will soon be part of the standard library o Some fixes to the cargo plugin o Update Source.set_ref() implementations according to last minute API correction o Fixed publishing of documentation o Some enhancements to the variables used in the autotools plugin ========================== buildstream-plugins 1.95.0 ========================== Initial beta release of buildstream-plugins. buildstream-plugins-2.7.0/NOTICE000066400000000000000000000002471515254461100164250ustar00rootroot00000000000000Apache BuildStream Copyright 2022 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). buildstream-plugins-2.7.0/README.rst000066400000000000000000000006731515254461100172130ustar00rootroot00000000000000BuildStream Plugins =================== A collection of plugins for the `BuildStream `_ project. How to use plugins ------------------ Plugins must be declared by your BuildStream project.conf for use in your project. For instructions on how to load plugins in your BuildStream project, please consult the `plugin loading documentation `_ buildstream-plugins-2.7.0/doc/000077500000000000000000000000001515254461100162635ustar00rootroot00000000000000buildstream-plugins-2.7.0/doc/Makefile000066400000000000000000000071261515254461100177310ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = -W SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source # Fix for when python is mapped to python2 not python3 # This is an issue in the sphinx-build script provided in the default install # because it uses the generic python env, so we need to have a copy of this script # but with an explicit call to python3. # # Why python3? We are using some features of sphinx that are only implemented # currently in python3 # PYV=$(shell python -c "import sys;t='{v[0]}'.format(v=list(sys.version_info[:2]));sys.stdout.write(t)") ifeq ($(PYV), 2) SPHINXBUILD = ./sphinx-build3 endif .PHONY: all clean templates html devhelp # Canned recipe for generating plugin api skeletons # $1 = the plugin directory # $2 = the output docs directory # # Explanation: # # Sphinx does not have any option for skipping documentation, # we don't want to document plugin code because nobody uses that # but we do want to use module-level docstrings in plugins in # order to explain how plugins work. # # For this purpose, we replace sphinx-apidoc with a simple # makefile rule which generates a template slightly differently # from how sphinx does it, allowing us to get what we want # from plugin documentation. # define plugin-doc-skeleton @for file in $$(find ${1}/${2} -name "*.py" ! -name "_*"); do \ base=$$(basename $$file); \ module=${2}.$${base%.py}; \ modname=$${base%.py}; \ echo -n "Generating source/${2}/$${modname}.rst... "; \ sed -e "s|@@MODULENAME@@|$${modname}|g" \ -e "s|@@MODULE@@|$${module}|g" \ source/plugin.rsttemplate > \ source/${2}/$${modname}.rst.tmp && \ mv source/${2}/$${modname}.rst.tmp source/${2}/$${modname}.rst || exit 1; \ echo "Done."; \ done endef # We set PYTHONPATH here because source/conf.py sys.modules hacks don't seem to help sphinx-build import the plugins all: html devhelp clean: templates-clean rm -rf build # Generate rst templates for the docs using a mix of sphinx-apidoc and # our 'plugin-doc-skeleton' routine for plugin pages. templates: mkdir -p source/elements mkdir -p source/sources mkdir -p source/sourcemirrors $(call plugin-doc-skeleton,$(CURDIR)/../src/buildstream_plugins,elements) $(call plugin-doc-skeleton,$(CURDIR)/../src/buildstream_plugins,sources) $(call plugin-doc-skeleton,$(CURDIR)/../src/buildstream_plugins,sourcemirrors) templates-clean: rm -rf source/elements rm -rf source/sources rm -rf source/sourcemirrors # Targets which generate docs with sphinx build # # html devhelp: templates @echo "Building $@..." PYTHONPATH=$(CURDIR)/../src/buildstream_plugins \ $(SPHINXBUILD) -b $@ $(ALLSPHINXOPTS) "$(BUILDDIR)/$@" \ $(wildcard source/*.rst) \ $(wildcard source/elements/*.rst) \ $(wildcard source/sources/*.rst) \ $(wildcard source/sourcemirrors/*.rst) @echo @echo "Build of $@ finished, output: $(CURDIR)/$(BUILDDIR)/$@" testy: @echo "Using $(SPHINXBUILD)" @echo "Py is $(PYV)" buildstream-plugins-2.7.0/doc/source/000077500000000000000000000000001515254461100175635ustar00rootroot00000000000000buildstream-plugins-2.7.0/doc/source/conf.py000066400000000000000000000234701515254461100210700ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # BuildStream documentation build configuration file, created by # sphinx-quickstart on Mon Nov 7 21:03:37 2016. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # import os import sys from buildstream_plugins import __version__ sys.path.insert(0, os.path.abspath('..')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.napoleon', 'sphinx.ext.extlinks', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['.templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The encoding of source files. # # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = 'BuildStream Plugins' copyright = '2022, The Apache Software Foundation' author = 'The Apache Software Foundation' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = __version__ # The full version, including alpha/beta/rc tags. release = __version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = "en" # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # # today = '' # # Else, today_fmt is used as the format for a strftime call. # # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. # # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = False # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. modindex_common_prefix = [ 'buildstream.' ] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. # " v documentation" by default. # # html_title = 'BuildStream v0.1' # A shorter title for the navigation bar. Default is the same as html_title. # # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # # html_logo = None # The name of an image file (relative to this directory) to use as a favicon of # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = [] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. # # html_extra_path = [] # If not None, a 'Last updated on:' timestamp is inserted at every page # bottom, using the given strftime format. # The empty string is equivalent to '%b %d, %Y'. # # html_last_updated_fmt = None # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # # html_additional_pages = {} # If false, no module index is generated. # # html_domain_indices = True # If false, no index is generated. # # html_use_index = True # If true, the index is split into individual pages for each letter. # # html_split_index = False # If true, links to the reST sources are added to the pages. # # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Language to be used for generating the HTML full-text search index. # Sphinx supports the following languages: # 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' # 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr', 'zh' # # html_search_language = 'en' # A dictionary with options for the search language support, empty by default. # 'ja' uses this config value. # 'zh' user can custom change `jieba` dictionary path. # # html_search_options = {'type': 'default'} # The name of a javascript file (relative to the configuration directory) that # implements a search results scorer. If empty, the default will be used. # # html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. htmlhelp_basename = 'BuildStreamdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'BuildStream.tex', 'BuildStream Plugins Documentation', 'BuildStream Developers', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # # latex_use_parts = False # If true, show page references after internal links. # # latex_show_pagerefs = False # If true, show URL addresses after external links. # # latex_show_urls = False # Documents to append as an appendix to all manuals. # # latex_appendices = [] # It false, will not define \strong, \code, itleref, \crossref ... but only # \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added # packages. # # latex_keep_old_macro_names = True # If false, no module index is generated. # # latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'buildstream', 'BuildStream Documentation', [author], 1) ] # If true, show URL addresses after external links. # # man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'BuildStream', 'BuildStream Plugins Documentation', author, 'BuildStream', 'A collection of BuildStream plugins.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. # # texinfo_appendices = [] # If false, no module index is generated. # # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # # texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. # # texinfo_no_detailmenu = False autodoc_member_order = 'bysource' buildstream-plugins-2.7.0/doc/source/index.rst000066400000000000000000000024571515254461100214340ustar00rootroot00000000000000.. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. .. toctree:: :maxdepth: 2 BuildStream Plugins Documentation ================================= This is a collection of plugins to use with Buildstream. To these plugins in your project, follow the `plugin loading documentation `_. .. toctree:: :maxdepth: 1 :caption: Element Plugins elements/autotools elements/cmake elements/make elements/meson elements/pip elements/setuptools .. toctree:: :maxdepth: 1 :caption: Source Plugins sources/bzr sources/cargo sources/docker sources/git sources/patch sources/pip .. toctree:: :maxdepth: 1 :caption: Source Mirror Plugins sourcemirrors/simple_mirror sourcemirrors/gitlab_lfs_mirror buildstream-plugins-2.7.0/doc/source/plugin.rsttemplate000066400000000000000000000001371515254461100233500ustar00rootroot00000000000000@@MODULENAME@@ plugin ============================================ .. automodule:: @@MODULE@@ buildstream-plugins-2.7.0/doc/sphinx-build3000077500000000000000000000004571515254461100207100ustar00rootroot00000000000000#!/usr/bin/python3 # -*- coding: utf-8 -*- """ Same as /usr/bin/sphinx-build but with different interpreter """ import sys if __name__ == '__main__': from sphinx import main, make_main if sys.argv[1:2] == ['-M']: sys.exit(make_main(sys.argv)) else: sys.exit(main(sys.argv))buildstream-plugins-2.7.0/project.conf000066400000000000000000000021221515254461100200300ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # This project.conf exposes the plugins as a buildstream project so # that plugins can be loaded via junctions. # name: buildstream-plugins min-version: 2.5 plugins: - origin: local path: src/buildstream_plugins/elements elements: - autotools - cmake - make - meson - pip - setuptools - origin: local path: src/buildstream_plugins/sources sources: - bzr - cargo - docker - git - patch - pip - origin: local path: src/buildstream_plugins/sourcemirrors source-mirrors: - gitlab_lfs_mirror - simple_mirror buildstream-plugins-2.7.0/pyproject.toml000066400000000000000000000017351515254461100204400ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # [build-system] requires = [ # We need at least version 36.6.0 that introduced "build_meta" "setuptools>=36.6.0", # In order to build wheels, and as required by PEP 517 "wheel", "Cython" ] build-backend = "setuptools.build_meta" [tool.black] line-length = 119 exclude = ''' ( /( \.eggs | \.git | \.mypy_cache | \.tox | _build | build | dist )/ | src/buildstream/_protos ) ''' buildstream-plugins-2.7.0/requirements/000077500000000000000000000000001515254461100202415ustar00rootroot00000000000000buildstream-plugins-2.7.0/requirements/mypy-requirements.txt000066400000000000000000000001171515254461100245200ustar00rootroot00000000000000# Additional requirements for running mypy # For docker plugin types-requests buildstream-plugins-2.7.0/requirements/plugin-requirements.txt000066400000000000000000000002611515254461100250200ustar00rootroot00000000000000# The dependencies listed here are necessary for specifc plugins, but # aren't required for buildstream-plugins to be installed. # Cargo source tomli # Docker source requests buildstream-plugins-2.7.0/requirements/test-requirements.txt000066400000000000000000000002671515254461100245070ustar00rootroot00000000000000build pytest-env # Provide option to run tests in parallel, less reliable pytest-xdist pytest >= 6.0.1 pytest-datafiles >= 3.0 pylint pycodestyle pyftpdlib responses setuptools wheel buildstream-plugins-2.7.0/setup.cfg000066400000000000000000000017351515254461100173450ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # [aliases] test=pytest [tool:pytest] addopts = --verbose --basetemp ./tmp --durations=20 testpaths = tests norecursedirs = tests/sources/pip-build integration-cache tmp __pycache__ .eggs python_files = tests/*.py markers = integration: run test only if --integration option is specified datafiles: share datafiles in tests env = D:BST_TEST_SUITE=True [mypy] files = src warn_unused_configs = True warn_no_return = True buildstream-plugins-2.7.0/setup.py000077500000000000000000000103171515254461100172350ustar00rootroot00000000000000#!/usr/bin/env python3 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # import os import sys try: from setuptools import setup, find_packages except ImportError: print( "BuildStream requires setuptools in order to locate plugins. Install " "it using your package manager (usually python3-setuptools) or via " "pip (pip3 install setuptools)." ) sys.exit(1) ############################################################################### # Parse README # ############################################################################### with open( os.path.join(os.path.dirname(os.path.realpath(__file__)), "README.rst"), encoding="utf-8", ) as readme: long_description = readme.read() ############################################################################### # Load the version # ############################################################################### sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src")) from buildstream_plugins import __version__ # pylint: disable=wrong-import-position setup( name="buildstream-plugins", version=__version__, author="The Apache Software Foundation", author_email="dev@buildstream.apache.org", classifiers=[ "Environment :: Console", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Operating System :: POSIX", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Topic :: Software Development :: Build Tools", ], description="A collection of plugins for BuildStream.", long_description=long_description, long_description_content_type="text/x-rst; charset=UTF-8", license="Apache License Version 2.0", url="https://buildstream.build", project_urls={ "Documentation": "https://apache.github.io/buildstream-plugins/", "Source": "https://github.com/apache/buildstream-plugins/", "Tracker": "https://github.com/apache/buildstream-plugins/issues", "Mailing List": "https://lists.apache.org/list.html?dev@buildstream.apache.org", }, package_dir={"": "src"}, packages=find_packages(where="src"), include_package_data=True, entry_points={ "buildstream.plugins.elements": [ "autotools = buildstream_plugins.elements.autotools", "cmake = buildstream_plugins.elements.cmake", "make = buildstream_plugins.elements.make", "meson = buildstream_plugins.elements.meson", "pip = buildstream_plugins.elements.pip", "setuptools = buildstream_plugins.elements.setuptools", ], "buildstream.plugins.sources": [ "bzr = buildstream_plugins.sources.bzr", "cargo = buildstream_plugins.sources.cargo", "docker = buildstream_plugins.sources.docker", "git = buildstream_plugins.sources.git", "patch = buildstream_plugins.sources.patch", "pip = buildstream_plugins.sources.pip", "zip = buildstream_plugins.sources.zip", ], "buildstream.plugins.sourcemirrors": [ "gitlab_lfs_mirror = buildstream_plugins.sourcemirrors.gitlab_lfs_mirror", "simple_mirror = buildstream_plugins.sourcemirrors.simple_mirror", ], }, extras_require={ "cargo": ['tomli; python_version < "3.11"'], }, zip_safe=False, ) # eof setup() buildstream-plugins-2.7.0/src/000077500000000000000000000000001515254461100163055ustar00rootroot00000000000000buildstream-plugins-2.7.0/src/buildstream_plugins/000077500000000000000000000000001515254461100223615ustar00rootroot00000000000000buildstream-plugins-2.7.0/src/buildstream_plugins/__init__.py000066400000000000000000000012111515254461100244650ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # Remember to adjust this version number before tagging releases # __version__ = "2.7.0" buildstream-plugins-2.7.0/src/buildstream_plugins/elements/000077500000000000000000000000001515254461100241755ustar00rootroot00000000000000buildstream-plugins-2.7.0/src/buildstream_plugins/elements/__init__.py000066400000000000000000000000001515254461100262740ustar00rootroot00000000000000buildstream-plugins-2.7.0/src/buildstream_plugins/elements/autotools.py000066400000000000000000000043711515254461100266050ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Authors: # Tristan Van Berkom # # This plugin was originally developped in the https://github.com/apache/buildstream/ # repository and was copied from 1a3c707a6c46573ab159de64ac9cd92e7f6027e6 # """ autotools - Autotools build element =================================== This is a :mod:`BuildElement ` implementation for using Autotools build scripts (also known as the `GNU Build System `_). You will often want to pass additional arguments to ``configure``. This should be done on a per-element basis by setting the ``conf-local`` variable. Here is an example: .. code:: yaml variables: conf-local: | --disable-foo --enable-bar If you want to pass extra options to ``configure`` for every element in your project, set the ``conf-global`` variable in your project.conf file. Here is an example of that: .. code:: yaml elements: autotools: variables: conf-global: | --disable-gtk-doc --disable-static Here is the default configuration for the ``autotools`` element in full: .. literalinclude:: ../../../src/buildstream_plugins/elements/autotools.yaml :language: yaml See `built-in functionality documentation `_ for details on common configuration options for build elements. """ from buildstream import BuildElement # Element implementation for the 'autotools' kind. class AutotoolsElement(BuildElement): # pylint: disable=attribute-defined-outside-init BST_MIN_VERSION = "2.0" # Plugin entry point def setup(): return AutotoolsElement buildstream-plugins-2.7.0/src/buildstream_plugins/elements/autotools.yaml000066400000000000000000000071331515254461100271160ustar00rootroot00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Autotools default configurations variables: autogen: | export NOCONFIGURE=1; if [ -x %{conf-cmd} ]; then true; elif [ -x %{conf-root}/autogen ]; then %{conf-root}/autogen; elif [ -x %{conf-root}/autogen.sh ]; then %{conf-root}/autogen.sh; elif [ -x %{conf-root}/bootstrap ]; then %{conf-root}/bootstrap; elif [ -x %{conf-root}/bootstrap.sh ]; then %{conf-root}/bootstrap.sh; else autoreconf -ivf %{conf-root}; fi # Project-wide extra arguments to be passed to `configure` conf-global: '' # Element-specific extra arguments to be passed to `configure`. conf-local: '' conf-cmd: "%{conf-root}/configure" conf-args: | --prefix=%{prefix} \ --exec-prefix=%{exec_prefix} \ --bindir=%{bindir} \ --sbindir=%{sbindir} \ --sysconfdir=%{sysconfdir} \ --datadir=%{datadir} \ --includedir=%{includedir} \ --libdir=%{libdir} \ --libexecdir=%{libexecdir} \ --localstatedir=%{localstatedir} \ --sharedstatedir=%{sharedstatedir} \ --mandir=%{mandir} \ --infodir=%{infodir} %{conf-global} %{conf-local} configure: | %{conf-cmd} %{conf-args} make-args: "" make-install-args: >- %{make-args} DESTDIR="%{install-root}" install make: make %{make-args} make-install: make -j1 %{make-install-args} # Set this if the sources cannot handle parallelization. # # notparallel: True # Automatically remove libtool archive files # # Set remove-libtool-modules to "true" to remove .la files for # modules intended to be opened with lt_dlopen() # # Set remove-libtool-libraries to "true" to remove .la files for # libraries # # Value must be "true" or "false" remove-libtool-modules: "false" remove-libtool-libraries: "false" delete-libtool-archives: | if %{remove-libtool-modules} || %{remove-libtool-libraries}; then find "%{install-root}" -name "*.la" -print0 | while read -d '' -r file; do if grep '^shouldnotlink=yes$' "${file}" &>/dev/null; then if %{remove-libtool-modules}; then echo "Removing ${file}." rm "${file}" else echo "Not removing ${file}." fi else if %{remove-libtool-libraries}; then echo "Removing ${file}." rm "${file}" else echo "Not removing ${file}." fi fi done fi config: # Commands for configuring the software # configure-commands: - | %{autogen} - | %{configure} # Commands for building the software # build-commands: - | %{make} # Commands for installing the software into a # destination folder # install-commands: - | %{make-install} - | %{delete-libtool-archives} # Commands for stripping debugging information out of # installed binaries # strip-commands: - | %{strip-binaries} # Use max-jobs CPUs for building and enable verbosity environment: MAKEFLAGS: -j%{max-jobs} V: 1 # And dont consider MAKEFLAGS or V as something which may # affect build output. environment-nocache: - MAKEFLAGS - V buildstream-plugins-2.7.0/src/buildstream_plugins/elements/cmake.py000066400000000000000000000043231515254461100256310ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Authors: # Tristan Van Berkom # # This plugin was originally developped in the https://gitlab.com/BuildStream/bst-plugins-experimental/ # repository and was copied from a60426126e5bec2d630fcd889a9f5af13af00ea6 # """ cmake - CMake build element =========================== This is a `BuildElement `_ implementation for using the `CMake `_ build system. You will often want to pass additional arguments to the ``cmake`` program for specific configuration options. This should be done on a per-element basis by setting the ``cmake-local`` variable. Here is an example: .. code:: yaml variables: cmake-local: | -DCMAKE_BUILD_TYPE=Debug If you want to pass extra options to ``cmake`` for every element in your project, set the ``cmake-global`` variable in your project.conf file. Here is an example of that: .. code:: yaml elements: cmake: variables: cmake-global: | -DCMAKE_BUILD_TYPE=Release Here is the default configuration for the ``cmake`` element in full: .. literalinclude:: ../../../src/buildstream_plugins/elements/cmake.yaml :language: yaml See `built-in functionality documentation `_ for details on common configuration options for build elements. """ from buildstream import BuildElement # Element implementation for the 'cmake' kind. class CMakeElement(BuildElement): BST_MIN_VERSION = "2.0" # Plugin entry point def setup(): return CMakeElement buildstream-plugins-2.7.0/src/buildstream_plugins/elements/cmake.yaml000066400000000000000000000036071515254461100261470ustar00rootroot00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # CMake default configuration variables: build-dir: _builddir # Project-wide extra arguments to be passed to `cmake` cmake-global: '' # Element-specific extra arguments to be passed to `cmake`. cmake-local: '' # The cmake generator to use generator: Ninja cmake-args: | -DCMAKE_VERBOSE_MAKEFILE=ON \ -DCMAKE_INSTALL_PREFIX:PATH="%{prefix}" \ -DCMAKE_INSTALL_LIBDIR:PATH="%{lib}" %{cmake-global} %{cmake-local} cmake: | cmake -B%{build-dir} -H"%{conf-root}" -G"%{generator}" %{cmake-args} make: cmake --build %{build-dir} -- ${JOBS} make-install: env DESTDIR="%{install-root}" cmake --build %{build-dir} --target install # Set this if the sources cannot handle parallelization. # # notparallel: True config: # Commands for configuring the software # configure-commands: - | %{cmake} # Commands for building the software # build-commands: - | %{make} # Commands for installing the software into a # destination folder # install-commands: - | %{make-install} # Commands for stripping debugging information out of # installed binaries # strip-commands: - | %{strip-binaries} # Use max-jobs CPUs for building and enable verbosity environment: JOBS: -j%{max-jobs} # And dont consider JOBS as something which may # affect build output. environment-nocache: - JOBS buildstream-plugins-2.7.0/src/buildstream_plugins/elements/make.py000066400000000000000000000031161515254461100254650ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Authors: # Ed Baunton # # This plugin was originally developped in the https://gitlab.com/BuildStream/bst-plugins-experimental/ # repository and was copied from a60426126e5bec2d630fcd889a9f5af13af00ea6 # """ make - Make build element ========================= This is a `BuildElement `_ implementation for using GNU make based build. Here is the default configuration for the ``make`` element in full: .. literalinclude:: ../../../src/buildstream_plugins/elements/make.yaml :language: yaml See `built-in functionality documentation `_ for details on common configuration options for build elements. """ from buildstream import BuildElement # Element implementation for the 'make' kind. class MakeElement(BuildElement): BST_MIN_VERSION = "2.0" # Plugin entry point def setup(): return MakeElement buildstream-plugins-2.7.0/src/buildstream_plugins/elements/make.yaml000066400000000000000000000026361515254461100260050ustar00rootroot00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # make default configurations variables: make-args: >- PREFIX="%{prefix}" make-install-args: >- %{make-args} DESTDIR="%{install-root}" install make: make %{make-args} make-install: make -j1 %{make-install-args} # Set this if the sources cannot handle parallelization. # # notparallel: True config: # Commands for building the software # build-commands: - | %{make} # Commands for installing the software into a # destination folder # install-commands: - | %{make-install} # Commands for stripping debugging information out of # installed binaries # strip-commands: - | %{strip-binaries} # Use max-jobs CPUs for building and enable verbosity environment: MAKEFLAGS: -j%{max-jobs} V: 1 # And dont consider MAKEFLAGS or V as something which may # affect build output. environment-nocache: - MAKEFLAGS - V buildstream-plugins-2.7.0/src/buildstream_plugins/elements/meson.py000066400000000000000000000041021515254461100256650ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This plugin was originally developped in the https://gitlab.com/BuildStream/bst-plugins-experimental/ # repository and was copied from a60426126e5bec2d630fcd889a9f5af13af00ea6 # """ meson - Meson build element =========================== This is a `BuildElement `_ implementation for using `Meson `_ build scripts. You will often want to pass additional arguments to ``meson``. This should be done on a per-element basis by setting the ``meson-local`` variable. Here is an example: .. code:: yaml variables: meson-local: | -Dmonkeys=yes If you want to pass extra options to ``meson`` for every element in your project, set the ``meson-global`` variable in your project.conf file. Here is an example of that: .. code:: yaml elements: meson: variables: meson-global: | -Dmonkeys=always Here is the default configuration for the ``meson`` element in full: .. literalinclude:: ../../../src/buildstream_plugins/elements/meson.yaml :language: yaml See `built-in functionality documentation `_ for details on common configuration options for build elements. """ from buildstream import BuildElement # Element implementation for the 'meson' kind. class MesonElement(BuildElement): BST_MIN_VERSION = "2.0" # Plugin entry point def setup(): return MesonElement buildstream-plugins-2.7.0/src/buildstream_plugins/elements/meson.yaml000066400000000000000000000040621515254461100262040ustar00rootroot00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Meson default configuration variables: build-dir: _builddir # Project-wide extra arguments to be passed to `meson` meson-global: '' # Element-specific extra arguments to be passed to `meson`. meson-local: '' meson-args: | --prefix=%{prefix} \ --bindir=%{bindir} \ --sbindir=%{sbindir} \ --sysconfdir=%{sysconfdir} \ --datadir=%{datadir} \ --includedir=%{includedir} \ --libdir=%{libdir} \ --libexecdir=%{libexecdir} \ --localstatedir=%{localstatedir} \ --sharedstatedir=%{sharedstatedir} \ --mandir=%{mandir} \ --infodir=%{infodir} %{meson-global} %{meson-local} meson: meson setup %{conf-root} %{build-dir} %{meson-args} meson-build: | ninja -v -j ${JOBS} -C %{build-dir} meson-install: | env DESTDIR="%{install-root}" meson install -C %{build-dir} --no-rebuild # Set this if the sources cannot handle parallelization. # # notparallel: True config: # Commands for configuring the software # configure-commands: - | %{meson} # Commands for building the software # build-commands: - | %{meson-build} # Commands for installing the software into a # destination folder # install-commands: - | %{meson-install} # Commands for stripping debugging information out of # installed binaries # strip-commands: - | %{strip-binaries} # Use max-jobs CPUs for building environment: JOBS: | %{max-jobs} # And dont consider JOBS as something which may # affect build output. environment-nocache: - JOBS buildstream-plugins-2.7.0/src/buildstream_plugins/elements/pip.py000066400000000000000000000030361515254461100253410ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Authors: # Mathieu Bridon # # This plugin was originally developped in the https://gitlab.com/BuildStream/bst-plugins-experimental/ # repository and was copied from a60426126e5bec2d630fcd889a9f5af13af00ea6 # """ pip - Pip build element ======================= A `BuildElement `_ implementation for installing Python modules with pip The pip default configuration: .. literalinclude:: ../../../src/buildstream_plugins/elements/pip.yaml :language: yaml See `built-in functionality documentation `_ for details on common configuration options for build elements. """ from buildstream import BuildElement # Element implementation for the 'pip' kind. class PipElement(BuildElement): BST_MIN_VERSION = "2.0" # Plugin entry point def setup(): return PipElement buildstream-plugins-2.7.0/src/buildstream_plugins/elements/pip.yaml000066400000000000000000000023471515254461100256570ustar00rootroot00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Pip default configurations variables: pip: pip pip-flags: | %{pip} install --no-deps --root=%{install-root} --prefix=%{prefix} pip-install-package: | %{pip-flags} %{conf-root} pip-download-dir: | .bst_pip_downloads pip-install-dependencies: | if [ -e %{pip-download-dir} ]; then %{pip-flags} %{pip-download-dir}/*; fi config: configure-commands: [] build-commands: [] # Commands for installing the software into a # destination folder # install-commands: - | %{pip-install-package} - | %{pip-install-dependencies} # Commands for stripping debugging information out of # installed binaries # strip-commands: - | %{strip-binaries} buildstream-plugins-2.7.0/src/buildstream_plugins/elements/setuptools.py000066400000000000000000000032131515254461100267670ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Authors: # Tristan Van Berkom # # This plugin was originally developped in the https://gitlab.com/BuildStream/bst-plugins-experimental/ # repository (previously named 'distutils') and was copied from a60426126e5bec2d630fcd889a9f5af13af00ea6 # """ setuptools - Python setuptools element ====================================== A `BuildElement `_ implementation for using python setuptools The setuptools default configuration: .. literalinclude:: ../../../src/buildstream_plugins/elements/setuptools.yaml :language: yaml See `built-in functionality documentation `_ for details on common configuration options for build elements. """ from buildstream import BuildElement # Element implementation for the python 'setuptools' kind. class SetuptoolsElement(BuildElement): BST_MIN_VERSION = "2.0" # Plugin entry point def setup(): return SetuptoolsElement buildstream-plugins-2.7.0/src/buildstream_plugins/elements/setuptools.yaml000066400000000000000000000025231515254461100273040ustar00rootroot00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Default python distutils configuration variables: # When building for python2 distutils, simply # override this in the element declaration python: python3 python-build: | %{python} %{conf-root}/setup.py build install-args: | --prefix "%{prefix}" \ --root "%{install-root}" python-install: | %{python} %{conf-root}/setup.py install %{install-args} config: # Commands for configuring the software # configure-commands: [] # Commands for building the software # build-commands: - | %{python-build} # Commands for installing the software into a # destination folder # install-commands: - | %{python-install} # Commands for stripping debugging information out of # installed binaries # strip-commands: - | %{strip-binaries} buildstream-plugins-2.7.0/src/buildstream_plugins/sourcemirrors/000077500000000000000000000000001515254461100252775ustar00rootroot00000000000000buildstream-plugins-2.7.0/src/buildstream_plugins/sourcemirrors/__init__.py000066400000000000000000000000001515254461100273760ustar00rootroot00000000000000buildstream-plugins-2.7.0/src/buildstream_plugins/sourcemirrors/gitlab_lfs_mirror.py000066400000000000000000000053571515254461100313630ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ gitlab_lfs_mirror - plugin for accessing files stored in git-lfs on GitLab ========================================================================== .. note:: The ``gitlab_lfs_mirror`` plugin is available *Since 2.4.0* If you store files in a git-lfs repository on gitlab.com, you can access them using a URL like https://gitlab.com/path/to/repo/-/raw/master/path/to/file which you can then use as a mirror for buildstream. However this only works for public repositories. For internal (on self-hosted GitLab instances) and private repositories, the above doesn't work since buildstream cannot authenticate with the GitLab web UI. This plugin solves this by going through the GitLab REST API. You need an access token that access the Repository Files API (i.e. with read_api or read_repository scope). As of this writing, the GitLab CI/CD job token doesn't allow access to this API endpoint, so you need a dedicated access token. **Usage:** .. code:: yaml - name: my-mirror kind: gitlab_lfs_mirror config: url: https://gitlab.example.com/ project: mirrors/{alias} ref: main # optional, defaults to master aliases: - my-alias - another-alias """ from posixpath import join from urllib.parse import quote from buildstream import SourceMirror class GitlabLFSMirror(SourceMirror): BST_MIN_VERSION = "2.2" def configure(self, node): node.validate_keys(["aliases", "url", "project", "ref"]) self.set_supported_aliases(node.get_str_list("aliases")) self.url = node.get_str("url") self.project = node.get_str("project") self.ref = node.get_str("ref", "master") def translate_url(self, *, alias, alias_url, source_url, extra_data): project_id = quote(self.project.format(alias=alias), safe="") filename = quote(source_url, safe="") translated_url = join( self.url, "api/v4/projects", project_id, "repository/files", filename, f"raw?ref={self.ref}&lfs=true", ) if extra_data is not None: extra_data["http-auth"] = "bearer" return translated_url def setup(): return GitlabLFSMirror buildstream-plugins-2.7.0/src/buildstream_plugins/sourcemirrors/simple_mirror.py000066400000000000000000000031551515254461100305400ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ simple_mirror - plugin for simplifying mirror definitions ========================================================= .. note:: The ``simple_mirror`` plugin is available *Since 2.4.0* **Usage:** .. code:: yaml - name: my-mirror kind: simple_mirror config: url: https://example.com/mirrors/{alias}/ aliases: - my-alias - another-alias This plugin simplifies defining mirrors for projects where the mirrors follow a predictable URL format that only varies with the alias name. """ from posixpath import join from buildstream import SourceMirror class SimpleMirror(SourceMirror): BST_MIN_VERSION = "2.2" def configure(self, node): node.validate_keys(["url", "aliases"]) self.set_supported_aliases(node.get_str_list("aliases")) self.url = node.get_str("url") def translate_url(self, *, alias, alias_url, source_url, extra_data): base_url = self.url.format(alias=alias) translated_url = join(base_url, source_url) return translated_url def setup(): return SimpleMirror buildstream-plugins-2.7.0/src/buildstream_plugins/sources/000077500000000000000000000000001515254461100240445ustar00rootroot00000000000000buildstream-plugins-2.7.0/src/buildstream_plugins/sources/__init__.py000066400000000000000000000000001515254461100261430ustar00rootroot00000000000000buildstream-plugins-2.7.0/src/buildstream_plugins/sources/_utils.py000066400000000000000000000102451515254461100257170ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Mostly copied from downloadablefilesource.py in buildstream. # import contextlib import shutil import os import netrc import urllib.parse import urllib.request import urllib.error class _NetrcPasswordManager: def __init__(self, netrc_config): self.netrc = netrc_config def add_password(self, realm, uri, user, passwd): pass def find_user_password(self, realm, authuri): if not self.netrc: return None, None parts = urllib.parse.urlsplit(authuri) entry = self.netrc.authenticators(parts.hostname) if not entry: return None, None else: login, _, password = entry return login, password def _parse_netrc(): netrc_config = None try: netrc_config = netrc.netrc() except (OSError, netrc.NetrcParseError): # If the .netrc file was not found, FileNotFoundError will be # raised, but OSError will be raised directly by the netrc package # in the case that $HOME is not set. # # This will catch both cases. pass return netrc_config class _UrlOpenerCreator: def __init__(self, netrc_config): self.netrc_config = netrc_config def get_url_opener(self, bearer_auth): if self.netrc_config and not bearer_auth: netrc_pw_mgr = _NetrcPasswordManager(self.netrc_config) http_auth = urllib.request.HTTPBasicAuthHandler(netrc_pw_mgr) return urllib.request.build_opener(http_auth) return urllib.request.build_opener() def download_file(url, etag, directory, auth_scheme): opener_creator = _UrlOpenerCreator(_parse_netrc()) opener = opener_creator.get_url_opener(auth_scheme == "bearer") default_name = os.path.basename(url) request = urllib.request.Request(url) request.add_header("Accept", "*/*") request.add_header("User-Agent", "BuildStream/2") if opener_creator.netrc_config and auth_scheme == "bearer": parts = urllib.parse.urlsplit(url) entry = opener_creator.netrc_config.authenticators(parts.hostname) if entry: _, _, password = entry auth_header = "Bearer " + password request.add_header("Authorization", auth_header) if etag is not None: request.add_header("If-None-Match", etag) try: with contextlib.closing(opener.open(request)) as response: info = response.info() # some servers don't honor the 'If-None-Match' header if etag and info["ETag"] == etag: return None, None, None etag = info["ETag"] length = info.get("Content-Length") filename = info.get_filename(default_name) filename = os.path.basename(filename) local_file = os.path.join(directory, filename) with open(local_file, "wb") as dest: shutil.copyfileobj(response, dest) actual_length = dest.tell() if length and actual_length < int(length): raise ValueError(f"Partial file {actual_length}/{length}") except urllib.error.HTTPError as e: if e.code == 304: # 304 Not Modified. # Because we use etag only for matching ref, currently specified ref is what # we would have downloaded. return None, None, None return None, None, str(e) except (urllib.error.URLError, OSError, ValueError) as e: # Note that urllib.request.Request in the try block may throw a # ValueError for unknown url types, so we handle it here. return None, None, str(e) return local_file, etag, None buildstream-plugins-2.7.0/src/buildstream_plugins/sources/bzr.py000066400000000000000000000241211515254461100252130ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Authors: # Jonathan Maw # # This plugin was originally developped in the https://github.com/apache/buildstream/ # repository and was copied from 1a3c707a6c46573ab159de64ac9cd92e7f6027e6 # """ bzr - stage files from a bazaar repository ========================================== **Host dependencies:** * bzr **Usage:** .. code:: yaml # Specify the bzr source kind kind: bzr # Specify the bzr url. Bazaar URLs come in many forms, see # `bzr help urlspec` for more information. Using an alias defined # in your project configuration is encouraged. url: https://launchpad.net/bzr # Specify the tracking branch. This is mandatory, as bzr cannot identify # an individual revision outside its branch. bzr URLs that omit the branch # name implicitly specify the trunk branch, but bst requires this to be # explicit. track: trunk # Specify the ref. This is a revision number. This is usually a decimal, # but revisions on a branch are of the form # .. # e.g. 6622.1.6. # The ref must be specified to build, and 'bst source track' will update the # revision number to the one on the tip of the branch specified in 'track'. ref: 6622 # Specify the version to be reported as the *guess_version* when reporting # SourceInfo # # Since 2.5 # version: 1.2 See `built-in functionality doumentation `_ for details on common configuration options for sources. Reporting `SourceInfo `_ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The bzr source reports the URL of the bzr repository as the *url*. Further, the bzr source reports the `SourceInfoMedium.BZR `_ *medium* and the` `SourceVersionType.COMMIT `_ *version_type*, for which it reports the bzr revision number as the *version*. Since the bzr source does not have a way to know what the release version corresponds to the revision number, the bzr source exposes the ``version`` configuration attribute to allow explicit specification of the *guess_version*. """ import os import shutil import fcntl from contextlib import contextmanager from buildstream import Source, SourceError from buildstream import utils # # Soft import of buildstream symbols only available in newer versions # # The BST_MIN_VERSION will provide a better user experience. # try: from buildstream import SourceInfoMedium, SourceVersionType except ImportError: pass class BzrSource(Source): # pylint: disable=attribute-defined-outside-init BST_MIN_VERSION = "2.5" def configure(self, node): node.validate_keys(["url", "track", "ref", "version", *Source.COMMON_CONFIG_KEYS]) self.original_url = node.get_str("url") self.tracking = node.get_str("track") self.ref = node.get_str("ref", None) self.url = self.translate_url(self.original_url) self.version = node.get_str("version", None) def preflight(self): # Check if bzr is installed, get the binary at the same time. self.host_bzr = utils.get_host_tool("bzr") def get_unique_key(self): unique_key = [self.original_url, self.tracking, self.ref] # Backwards compatible method of supporting configuration # attributes which affect SourceInfo generation. if self.version is not None: unique_key.append(self.version) return unique_key def is_cached(self): with self._locked(): return self._check_ref() def load_ref(self, node): self.ref = node.get_str("ref", None) def get_ref(self): return self.ref def set_ref(self, ref, node): node["ref"] = self.ref = ref def track(self): # pylint: disable=arguments-differ with self.timed_activity("Tracking {}".format(self.url), silent_nested=True), self._locked(): self._ensure_mirror(skip_ref_check=True) ret, out = self.check_output( [ self.host_bzr, "version-info", "--custom", "--template={revno}", self._get_branch_dir(), ], fail="Failed to read the revision number at '{}'".format(self._get_branch_dir()), ) if ret != 0: raise SourceError("{}: Failed to get ref for tracking {}".format(self, self.tracking)) return out def fetch(self): # pylint: disable=arguments-differ with self.timed_activity("Fetching {}".format(self.url), silent_nested=True), self._locked(): self._ensure_mirror() def stage(self, directory): self.call( [ self.host_bzr, "checkout", "--lightweight", "--revision=revno:{}".format(self.ref), self._get_branch_dir(), directory, ], fail="Failed to checkout revision {} from branch {} to {}".format( self.ref, self._get_branch_dir(), directory ), ) # Remove .bzr dir shutil.rmtree(os.path.join(directory, ".bzr")) def init_workspace(self, directory): url = os.path.join(self.url, self.tracking) with self.timed_activity('Setting up workspace "{}"'.format(directory), silent_nested=True): # Checkout from the cache self.call( [ self.host_bzr, "branch", "--use-existing-dir", "--revision=revno:{}".format(self.ref), self._get_branch_dir(), directory, ], fail="Failed to branch revision {} from branch {} to {}".format( self.ref, self._get_branch_dir(), directory ), ) # Switch the parent branch to the source's origin self.call( [ self.host_bzr, "switch", "--directory={}".format(directory), url, ], fail="Failed to switch workspace's parent branch to {}".format(url), ) def collect_source_info(self): return [ self.create_source_info( self.url, SourceInfoMedium.BAZAAR, SourceVersionType.COMMIT, self.ref, version_guess=self.version ) ] # _locked() # # This context manager ensures exclusive access to the # bzr repository. # @contextmanager def _locked(self): lockdir = os.path.join(self.get_mirror_directory(), "locks") lockfile = os.path.join(lockdir, utils.url_directory_name(self.original_url) + ".lock") os.makedirs(lockdir, exist_ok=True) with open(lockfile, "wb") as lock: fcntl.flock(lock, fcntl.LOCK_EX) try: yield finally: fcntl.flock(lock, fcntl.LOCK_UN) def _check_ref(self): # If the mirror doesnt exist yet, then we dont have the ref if not os.path.exists(self._get_branch_dir()): return False return ( self.call( [ self.host_bzr, "revno", "--revision=revno:{}".format(self.ref), self._get_branch_dir(), ] ) == 0 ) def _get_branch_dir(self): return os.path.join(self._get_mirror_dir(), self.tracking) def _get_mirror_dir(self): return os.path.join( self.get_mirror_directory(), utils.url_directory_name(self.original_url), ) def _ensure_mirror(self, skip_ref_check=False): mirror_dir = self._get_mirror_dir() bzr_metadata_dir = os.path.join(mirror_dir, ".bzr") if not os.path.exists(bzr_metadata_dir): self.call( [self.host_bzr, "init-repo", "--no-trees", mirror_dir], fail="Failed to initialize bzr repository", ) branch_dir = os.path.join(mirror_dir, self.tracking) branch_url = self.url + "/" + self.tracking if not os.path.exists(branch_dir): # `bzr branch` the branch if it doesn't exist # to get the upstream code self.call( [self.host_bzr, "branch", branch_url, branch_dir], fail="Failed to branch from {} to {}".format(branch_url, branch_dir), ) else: # `bzr pull` the branch if it does exist # to get any changes to the upstream code self.call( [ self.host_bzr, "pull", "--directory={}".format(branch_dir), branch_url, ], fail="Failed to pull new changes for {}".format(branch_dir), ) if not skip_ref_check and not self._check_ref(): raise SourceError( "Failed to ensure ref '{}' was mirrored".format(self.ref), reason="ref-not-mirrored", ) def setup(): return BzrSource buildstream-plugins-2.7.0/src/buildstream_plugins/sources/cargo.py000066400000000000000000000461531515254461100255220ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Authors: # Tristan Van Berkom # # This plugin was originally developped in the https://gitlab.com/BuildStream/bst-plugins-experimental/ # repository and was copied from a60426126e5bec2d630fcd889a9f5af13af00ea6 # """ cargo - Automatically stage crate dependencies ============================================== A convenience Source element for vendoring rust project dependencies. Placing this source in the source list, after a source which stages a Cargo.lock file, will allow this source to read the Cargo.lock file and obtain the crates automatically into %{vendordir}. Set the `ref` field to an empty list like so: `ref: []`, and then run `bst source track path_to_bst_file.bst`. **Usage:** .. code:: yaml # Specify the cargo source kind kind: cargo # Url of the crates repository to download from (default: https://static.crates.io/crates) url: https://static.crates.io/crates # Internal source reference, this is a list of dictionaries # which store the crate names and versions. # # This will be automatically updated with `bst source track` ref: - name: packagename version: 1.2.1 - name: packagename version: 1.3.0 # Specify a directory for the vendored crates (defaults to ./crates) vendor-dir: crates # Optionally specify the name of the lock file to use (defaults to Cargo.lock) cargo-lock: Cargo.lock See `built-in functionality doumentation `_ for details on common configuration options for sources. Reporting `SourceInfo `_ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The cargo source reports the URL of the archive crates as the *url* for each crate. Further, the cargo source reports the `SourceInfoMedium.REMOTE_FILE `_ *medium* and the `SourceVersionType.SHA256 `_ *version_type*, for which it reports the checksum of the archive as the *version*. The versions extracted from the Cargo.lock at tracking time are used to report the *guess_version*. Additional source info can be specified using the `provenance` field for each referenced crate as per `built-in source provenance documentation `_. """ import json import os.path import tarfile # We prefer tomli that was put into standard library as tomllib # starting from 3.11 try: import tomllib # type: ignore except ImportError: import tomli as tomllib # type: ignore from buildstream import Source, SourceFetcher, SourceError from buildstream import utils from ._utils import download_file # # Soft import of buildstream symbols only available in newer versions # # The BST_MIN_VERSION will provide a better user experience. # try: from buildstream import SourceInfoMedium, SourceVersionType except ImportError: pass # This automatically goes into .cargo/config # _default_vendor_config_template = ( "[source.crates-io]\n" + 'registry = "{vendorurl}"\n' + 'replace-with = "vendored-sources"\n' + "[source.vendored-sources]\n" + 'directory = "{vendordir}"\n' ) # Crate() # # Use a SourceFetcher class to be the per crate helper # # Args: # cargo (Cargo): The main Source implementation # name (str): The name of the crate to depend on # version (str): The version of the crate to depend on # sha (str|None): The sha256 checksum of the downloaded crate # class Crate(SourceFetcher): def __init__(self, cargo, name, version, sha=None, provenance=None): super().__init__() self.cargo = cargo self.name = name self.version = str(version) self.sha = sha self.provenance = provenance self.mark_download_url(cargo.url) ######################################################## # SourceFetcher API method implementations # ######################################################## def fetch(self, alias_override=None, **kwargs): # Just a defensive check, it is impossible for the # file to be already cached because Source.fetch() will # not be called if the source is already cached. # if os.path.isfile(self._get_mirror_file()): return # pragma: nocover # Download the crate crate_url, auth_scheme = self._get_url(alias_override) with self.cargo.timed_activity("Downloading: {}".format(crate_url), silent_nested=True): sha256 = self._download(crate_url, auth_scheme) if self.sha is not None and sha256 != self.sha: raise SourceError( "File downloaded from {} has sha256sum '{}', not '{}'!".format(crate_url, sha256, self.sha) ) def get_source_info(self): url, _ = self._get_url() return self.cargo.create_source_info( url, SourceInfoMedium.REMOTE_FILE, SourceVersionType.SHA256, self.sha, version_guess=self.version, provenance_node=self.provenance, ) ######################################################## # Helper APIs for the Cargo Source to use # ######################################################## # stage() # # A delegate method to do the work for a single crate # in Source.stage(). # # Args: # (directory): The vendor subdirectory to stage to # def stage(self, directory): try: mirror_file = self._get_mirror_file() with tarfile.open(mirror_file) as tar: members = tar.getmembers() if hasattr(tarfile, "tar_filter"): # Python 3.12+ (and older versions with backports) tar.extractall(path=directory, filter="tar") else: for member in members: self._assert_tarinfo_safe(member, directory) tar.extractall(path=directory, members=members) if members: dirname = members[0].name.split("/")[0] package_dir = os.path.join(directory, dirname) checksum_file = os.path.join(package_dir, ".cargo-checksum.json") with open(checksum_file, "w", encoding="utf-8") as f: checksum_data = {"package": self.sha, "files": {}} json.dump(checksum_data, f) except (tarfile.TarError, OSError) as e: raise SourceError("{}: Error staging source: {}".format(self, e)) from e # is_cached() # # Get whether we have a local cached version of the source # # Returns: # (bool): Whether we are cached or not # def is_cached(self): return os.path.isfile(self._get_mirror_file()) # is_resolved() # # Get whether the current crate is resolved # # Returns: # (bool): Whether we have a sha or not # def is_resolved(self): return self.sha is not None ######################################################## # Private helpers # ######################################################## # Assert that a tarfile is safe to extract; specifically, make # sure that we don't do anything outside of the target # directory (this is possible, if, say, someone engineered a # tarfile to contain paths that start with ..). def _assert_tarinfo_safe(self, member: tarfile.TarInfo, target_dir: str): final_path = os.path.abspath(os.path.join(target_dir, member.path)) if not final_path.startswith(target_dir): raise SourceError( "{}: Tarfile attempts to extract outside the staging area: " "{} -> {}".format(self, member.path, final_path) ) if member.islnk(): linked_path = os.path.abspath(os.path.join(target_dir, member.linkname)) if not linked_path.startswith(target_dir): raise SourceError( "{}: Tarfile attempts to hardlink outside the staging area: " "{} -> {}".format(self, member.path, final_path) ) # Don't need to worry about symlinks because they're just # files here and won't be able to do much harm once we are # in a sandbox. # _download() # # Downloads the crate from the url and caches it. # # Args: # url (str): The url to download from # # Returns: # (str): The sha256 checksum of the downloaded crate # def _download(self, url, auth_scheme): # We do not use etag in case what we have in cache is # not matching ref in order to be able to recover from # corrupted download. if self.sha: etag = self._get_etag(self.sha) else: etag = None with self.cargo.tempdir() as td: local_file, etag, error = download_file(url, etag, td, auth_scheme) if error: raise SourceError("{}: Error mirroring {}: {}".format(self, url, error), temporary=True) # Make sure url-specific mirror dir exists. os.makedirs(self._get_mirror_dir(), exist_ok=True) # Store by sha256sum sha256 = utils.sha256sum(local_file) # Even if the file already exists, move the new file over. # In case the old file was corrupted somehow. os.rename(local_file, self._get_mirror_file(sha256)) if etag: self._store_etag(sha256, etag) return sha256 # _get_url() # # Fetches the URL to download this crate from # # Args: # alias (str|None): The URL alias to apply, if any # # Returns: # (str): The URL for this crate # def _get_url(self, alias=None): path = "{name}/{name}-{version}.crate".format(name=self.name, version=self.version) extra_data = {} if utils.get_bst_version() >= (2, 2): translated_url = self.cargo.translate_url( self.cargo.url, suffix=path, alias_override=alias, extra_data=extra_data ) else: translated_url = self.cargo.translate_url(self.cargo.url, alias_override=alias) + path return translated_url, extra_data.get("http-auth") # _get_etag() # # Fetches the locally stored ETag information for this # crate's download. # # Args: # sha (str): The sha256 checksum of the downloaded crate # # Returns: # (str|None): The ETag to use for requests, or None if nothing is # locally downloaded # def _get_etag(self, sha): etagfilename = os.path.join(self._get_mirror_dir(), "{}.etag".format(sha)) if os.path.exists(etagfilename): with open(etagfilename, "r", encoding="utf-8") as etagfile: return etagfile.read() return None # _store_etag() # # Stores the locally cached ETag information for this crate. # # Args: # sha (str): The sha256 checksum of the downloaded crate # etag (str): The ETag to use for requests of this crate # def _store_etag(self, sha, etag): etagfilename = os.path.join(self._get_mirror_dir(), "{}.etag".format(sha)) with utils.save_file_atomic(etagfilename) as etagfile: etagfile.write(etag) # _get_mirror_dir() # # Gets the local mirror directory for this upstream cargo repository # def _get_mirror_dir(self): return os.path.join( self.cargo.get_mirror_directory(), utils.url_directory_name(self.cargo.url), self.name, self.version, ) # _get_mirror_file() # # Gets the local mirror filename for this crate # # Args: # sha (str|None): The sha256 checksum of the downloaded crate # def _get_mirror_file(self, sha=None): return os.path.join(self._get_mirror_dir(), sha or self.sha) class CargoSource(Source): BST_MIN_VERSION = "2.5" # We need the Cargo.lock file to construct our ref at track time BST_REQUIRES_PREVIOUS_SOURCES_TRACK = True BST_CUSTOM_SOURCE_PROVENANCE = True ######################################################## # Plugin/Source API method implementations # ######################################################## def configure(self, node): # The url before any aliasing # self.original_url = node.get_str("url", "https://static.crates.io/crates") self.cargo_lock = node.get_str("cargo-lock", "Cargo.lock") self.vendor_dir = node.get_str("vendor-dir", "crates") self.provenance = {} # Because source provenance info lives in the ref it would be # removed upon source track, create a backup here so it is # available to be added back in when setting the ref ref = node.get_sequence("ref", None) if ref is not None: for crate in ref: if "provenance" in crate: crate_name = crate.get_str("name") provenance = crate.get_mapping("provenance") self.provenance[crate_name] = provenance.strip_node_info() # If the specified URL is just an alias, require the alias to resolve # to a URL with a trailing slash. Otherwise, append a trailing slash if # it's missing, for backward compatibility. self.url = self.original_url if not self.url.endswith(":") and not self.url.endswith("/"): self.url += "/" node.validate_keys(Source.COMMON_CONFIG_KEYS + ["url", "ref", "cargo-lock", "vendor-dir"]) # Needs to be marked here so that `track` can translate it later. self.mark_download_url(self.url) self.load_ref(node) def preflight(self): return def get_unique_key(self): return [self.original_url, self.cargo_lock, self.vendor_dir, self.ref] def is_resolved(self): return (self.ref is not None) and all(crate.is_resolved() for crate in self.crates) def is_cached(self): return all(crate.is_cached() for crate in self.crates) def load_ref(self, node): ref = node.get_sequence("ref", None) self._recompute_crates(ref) def get_ref(self): return self.ref def set_ref(self, ref, node): def add_provenance(crate): if crate["name"] in self.provenance: crate["provenance"] = self.provenance[crate["name"]] return crate # add provenance back in ref = list(map(add_provenance, ref)) node["ref"] = ref self._recompute_crates(node.get_sequence("ref")) def track(self, *, previous_sources_dir): new_ref = [] lockfile = os.path.join(previous_sources_dir, self.cargo_lock) try: with open(lockfile, "rb") as f: try: lock = tomllib.load(f) except tomllib.TOMLDecodeError as e: raise SourceError( "Malformed Cargo.lock file at: {}".format(self.cargo_lock), detail="{}".format(e), ) from e except FileNotFoundError as e: raise SourceError( "Failed to find Cargo.lock file at: {}".format(self.cargo_lock), detail="The cargo plugin expects to find a Cargo.lock file in\n" + "the sources staged before it in the source list, but none was found.", ) from e # FIXME: Better validation would be good here, so we can raise more # useful error messages in the case of a malformed Cargo.lock file. # for package in lock["package"]: if "source" not in package: continue new_ref += [{"name": package["name"], "version": str(package["version"]), "sha": package.get("checksum")}] # Make sure the order we set it at track time is deterministic new_ref = sorted(new_ref, key=lambda c: (c["name"], c["version"])) # Download the crates and get their shas for crate_obj in new_ref: if crate_obj["sha"] is not None: continue crate = Crate(self, crate_obj["name"], crate_obj["version"]) crate_url, auth_scheme = crate._get_url() with self.timed_activity("Downloading: {}".format(crate_url), silent_nested=True): crate_obj["sha"] = crate._download(crate_url, auth_scheme) return new_ref def stage(self, directory): # Stage the crates into the vendor directory vendor_dir = os.path.join(directory, self.vendor_dir) for crate in self.crates: crate.stage(vendor_dir) # Stage our vendor config vendor_config = _default_vendor_config_template.format( vendorurl=self.translate_url(self.url), vendordir=self.vendor_dir ) conf_dir = os.path.join(directory, ".cargo") conf_file = os.path.join(conf_dir, "config") os.makedirs(conf_dir, exist_ok=True) with open(conf_file, "w", encoding="utf-8") as f: f.write(vendor_config) def get_source_fetchers(self): return self.crates ######################################################## # Private helpers # ######################################################## def _recompute_crates(self, ref): self.crates = self._parse_crates(ref) if not self.crates: self.ref = None else: self.ref = [{"name": crate.name, "version": crate.version, "sha": crate.sha} for crate in self.crates] # _parse_crates(): # # Generates a list of crates based on the passed ref # # Args: # (list|None) refs: The list of name/version dictionaries # # Returns: # (list): A list of Crate objects # def _parse_crates(self, refs): # Return an empty list for no ref if refs is None: return [] return [ Crate( self, crate.get_str("name"), crate.get_str("version"), sha=crate.get_str("sha", None), provenance=crate.get_mapping("provenance", None), ) for crate in refs ] def setup(): return CargoSource buildstream-plugins-2.7.0/src/buildstream_plugins/sources/docker.py000066400000000000000000000715261515254461100257000ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Authors: # Sam Thursfield # Chandan Singh # # This plugin was originally developped in the https://gitlab.com/BuildStream/bst-plugins-container/ # repository and was copied from 192aa75a62161b655b051d5b69f17d9233cf4dfe # """ docker - stage files from Docker images ======================================= **Usage:** .. code:: yaml # Specify the docker source kind kind: docker # Specify the registry endpoint, defaults to Docker Hub (optional) registry-url: https://registry.hub.docker.com # Image path image: library/alpine # Alternatively, you can specify a full url, the registry endpoint # is assumed to be at the root of the domain # url: https://registry.hub.docker.com/library/alpine # Image tag to follow (optional) track: latest # Specify the digest of the exact image to use (required) ref: 6c9f6f68a131ec6381da82f2bff978083ed7f4f7991d931bfa767b7965ebc94b # Some images are built for multiple platforms. When tracking a tag, we # will choose which image to use based on these settings. Default values # are chosen based on the output of `uname -m` and `uname -s`, but you # can override them. # # architecture: arm64 # os: linux # # **Since**: 2.0.1 # Specify the version to be reported as the *guess_version* when reporting # SourceInfo # # Since: 2.5 # version: 1.2 Note that Docker images may contain device nodes. BuildStream elements cannot contain device nodes so those will be dropped. Any regular files in the /dev directory will also be dropped. See `built-in functionality doumentation `_ for details on common configuration options for sources. Reporting `SourceInfo `_ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The docker source reports the URL of the docker registry as the *url*. Further, the docker source reports the `SourceInfoMedium.OCI_IMAGE `_ *medium* and the `SourceVersionType.OCI_DIGEST `_ *version_type*, for which it reports the content digest of the docker image as the *version*. Additionally, the docker source reports the docker image name through the ``image-name`` key of the *extra_data*. As such, after removing the scheme from the URL (i.e. remove ``https://``) the same docker image can be obtained by calling: .. code:: bash docker pull /@ Since the docker source does not have any way to guess what tag is associated to the digest, or what release version that would mean; the docker source exposes the ``version`` configuration attribute to allow explicit specification of the *guess_version*. """ import hashlib import json import os import platform import shutil import tarfile import urllib.parse from collections import OrderedDict import requests from buildstream import Source, SourceError from buildstream.utils import ( save_file_atomic, sha256sum, link_files, move_atomic, get_umask, ) # # Soft import of buildstream symbols only available in newer versions # # The BST_MIN_VERSION will provide a better user experience. # try: from buildstream import SourceInfoMedium, SourceVersionType except ImportError: pass _DOCKER_HUB_URL = "https://registry.hub.docker.com" def parse_bearer_authorization_challenge(text): # Hand-written and probably broken parsing of the Www-Authenticate # response. I can't find a built-in way to parse this, but I probably # didn't look hard enough. if not text.startswith("Bearer "): raise SourceError("Unexpected Www-Authenticate response: %{}".format(text)) pairs = {} text = text[len("Bearer ") :] for pair in text.split(","): key, value = pair.split("=") pairs[key] = value[1:-1] return pairs def default_architecture(): machine = platform.machine() if machine == "x86_64": return "amd64" elif machine == "aarch64": return "arm64" else: return machine def default_os(): return platform.system().lower() # Variant of urllib.parse.urljoin() allowing multiple path components at once. def urljoin(url, *args): for arg in args: if not url.endswith("/"): url += "/" url = urllib.parse.urljoin(url, arg.lstrip("/")) return url # Handles authentication with a bearer token class BearerAuth(requests.auth.AuthBase): def __init__(self, api_timeout=3): self.token = None self.api_timeout = api_timeout def __call__(self, r): if self.token: r.headers["Authorization"] = "Bearer {}".format(self.token) return r def refresh_token(self, auth_challenge): auth_vars = parse_bearer_authorization_challenge(auth_challenge) # Respond to an Www-Authenticate challenge by requesting the necessary # token from the 'realm' (endpoint) that we were given in the challenge. request_url = "{realm}?service={service}&scope={scope}".format(**auth_vars) response = requests.get(request_url, timeout=self.api_timeout) response.raise_for_status() self.token = response.json()["token"] # DockerManifestError # # Raised if something goes wrong while querying an image manifest from a remote # registry. # class DockerManifestError(SourceError): def __init__(self, message, manifest=None): super().__init__(message) self.manifest = manifest class DockerRegistryV2Client: def __init__(self, endpoint, api_timeout=3): self.endpoint = endpoint self.api_timeout = api_timeout self.auth = BearerAuth(api_timeout) def _request(self, subpath, extra_headers=None, stream=False, _reauthorized=False): if not extra_headers: extra_headers = {} headers = {"content-type": "application/json"} headers.update(extra_headers) url = urljoin(self.endpoint, "v2", subpath) response = requests.get(url, headers=headers, stream=stream, timeout=self.api_timeout, auth=self.auth) if response.status_code == requests.codes["unauthorized"] and not _reauthorized: # This request requires (re)authorization. See: # https://docs.docker.com/registry/spec/auth/token/ auth_challenge = response.headers["Www-Authenticate"] self.auth.refresh_token(auth_challenge) return self._request(subpath, extra_headers=extra_headers, _reauthorized=True) else: response.raise_for_status() return response # digest(): # # Calculate a Docker-compatible digest of an arbitrary string of bytes. # # Args: # content (bytes): Content to hash # # Returns: # (str) A Docker-compatible digest of 'content' @staticmethod def digest(content): digest_hash = hashlib.sha256() digest_hash.update(content) return "sha256:" + digest_hash.hexdigest() # manifest(): # # Fetches the image manifest for a given image from the remote registry. # # If this is a "fat" (multiplatform) image, the 'artitecture' and 'os' # parameters control which of the available images is chosen. # # The manifest is returned verbatim, so you need to parse it yourself # with json.loads() to get at its contents. The verbatim text can be used # to recalculate the content digest, just encode it and pass to .digest(). # If we returned only the parsed JSON data you wouldn't be able to do this. # # Args: # image_path (str): Relative path to the image, e.g. library/alpine # reference (str): Either a tag name (such as 'latest') or the content # digest of an exact version of the image. # architecture (str): Architecture name (amd64, arm64, etc.) # os_ (str): OS name (e.g. linux) # # Raises: # requests.RequestException, if network errors occur # # Returns: # (str, str): A tuple of the manifest content as text, and its content hash def manifest( self, image_path, reference, architecture, os_, ): # pylint: disable=too-many-locals accept_types = [ "application/vnd.docker.distribution.manifest.v2+json", "application/vnd.docker.distribution.manifest.list.v2+json", ] manifest_url = urljoin(image_path, "manifests", urllib.parse.quote(reference)) response = self._request(manifest_url, extra_headers={"Accept": ",".join(accept_types)}) try: manifest = json.loads(response.text) except json.JSONDecodeError as e: raise DockerManifestError( "Server did not return a valid manifest: {}".format(e), manifest=response.text, ) from e schema_version = manifest.get("schemaVersion") if schema_version == 1: raise DockerManifestError("Schema version 1 is unsupported.", manifest=response.text) if schema_version is None: raise DockerManifestError( "Manifest did not include the schemaVersion key.", manifest=response.text, ) our_digest = self.digest(response.text.encode("utf8")) their_digest = response.headers.get("Docker-Content-Digest") if not their_digest: raise DockerManifestError( "Server did not set the Docker-Content-Digest header.", manifest=response.text, ) if our_digest != their_digest: raise DockerManifestError( "Server returned a non-matching content digest. " "Our digest: {}, their digest: {}".format(our_digest, their_digest), manifest=response.text, ) if manifest["mediaType"] == "application/vnd.docker.distribution.manifest.list.v2+json": # This is a "fat manifest", we need to narrow down to a specific # architecture. for sub in manifest["manifests"]: if sub["platform"]["architecture"] == architecture and sub["platform"]["os"]: sub_digest = sub["digest"] return self.manifest( image_path, sub_digest, architecture=architecture, os_=os_, ) raise DockerManifestError( "No images found for architecture {}, OS {}".format(architecture, os_), manifest=response.text, ) if manifest["mediaType"] == "application/vnd.docker.distribution.manifest.v2+json": return response.text, our_digest else: raise DockerManifestError( "Unsupported manifest type {}".format(manifest["mediaType"]), manifest=response.text, ) # blob(): # # Fetch a blob from the remote registry. This is used for getting each # layer of an image in tar.gz format. # # Raises: # requests.RequestException, if network errors occur # # Args: # image_path (str): Relative path to the image, e.g. library/alpine # blob_digest (str): Content hash of the blob. # download_to (str): Path to a file where the content will be written. def blob(self, image_path, blob_digest, download_to): blob_url = urljoin(image_path, "blobs", urllib.parse.quote(blob_digest)) response = self._request(blob_url, stream=True) with save_file_atomic(download_to, "wb") as f: shutil.copyfileobj(response.raw, f) class ReadableTarInfo(tarfile.TarInfo): """ The goal is to override `TarFile`'s `extractall` semantics by ensuring that on extraction, the files are readable by the owner of the file. This is done by overriding the accessor for the `mode` attribute in `TarInfo`, the class that encapsulates the internal meta-data of the tarball, so that the owner-read bit is always set. """ # https://github.com/python/mypy/issues/4125 @property # type: ignore def mode(self): # Respect umask instead of the file mode stored in the archive. # The only bit used from the embedded mode is the executable bit for files. umask = get_umask() if self.isdir() or bool(self.__permission & 0o100): return 0o777 & ~umask else: return 0o666 & ~umask @mode.setter def mode(self, permission): self.__permission = permission # pylint: disable=attribute-defined-outside-init class DockerSource(Source): # pylint: disable=too-many-instance-attributes BST_MIN_VERSION = "2.5" # Docker identifies images by a content digest calculated from the image's # manifest. This corresponds well with the concept of a 'ref' in # BuildStream. However, Docker theoretically supports multiple hash # methods while BuildStream does not. Right now every Docker registry # uses sha256 so let's ignore that issue for the time being. @staticmethod def _digest_to_ref(digest): if digest.startswith("sha256:"): return digest[len("sha256:") :] else: method = digest.split(":")[0] raise SourceError("Unsupported digest method: {}".format(method)) @staticmethod def _ref_to_digest(ref): return "sha256:" + ref def configure(self, node): # url is deprecated, but accept it as a valid key so that we can raise # a nicer warning. node.validate_keys( Source.COMMON_CONFIG_KEYS + ["architecture", "registry-url", "image", "os", "ref", "track", "url", "version"] ) if "url" in node and ("image" in node or "registry-url" in node): raise SourceError("{}: May specify either 'url', or 'image' and 'registry-url'".format(self)) if "url" not in node and "image" not in node: raise SourceError("{}: Must define either 'url' or 'image'".format(self)) if "url" in node: self.url = node.get_str("url") translated_url = self.translate_url(self.url) scheme, netloc, path, _, _ = urllib.parse.urlsplit(translated_url) self.registry_url = urllib.parse.urlunsplit((scheme, netloc, "", "", "")) self.image = path if "image" in node: self.url = None self.image = node.get_str("image") self.original_registry_url = node.get_str("registry-url", _DOCKER_HUB_URL) self.registry_url = self.translate_url(self.original_registry_url) self.tag = node.get_str("track", "") or None self.architecture = node.get_str("architecture", "") or default_architecture() self.os = node.get_str("os", "") or default_os() self.version = node.get_str("version", None) self.digest = None self.load_ref(node) self.client = DockerRegistryV2Client(self.registry_url) self.manifest = None def load_ref(self, node): ref = node.get_str("ref", None) if ref is not None: self.digest = self._ref_to_digest(ref) def preflight(self): return def get_unique_key(self): if self.url is not None: unique_key = [self.url, self.digest] else: unique_key = [self.original_registry_url, self.image, self.digest] # Backwards compatible method of supporting configuration # attributes which affect SourceInfo generation. if self.version is not None: unique_key.append(self.version) return unique_key def get_ref(self): return None if self.digest is None else self._digest_to_ref(self.digest) def set_ref(self, ref, node): node["ref"] = ref self.digest = self._ref_to_digest(ref) def track(self): # pylint: disable=arguments-differ # If the tracking ref is not specified it's not an error, just silently return if not self.tag: return None with self.timed_activity( "Fetching image manifest for image: '{}:{}' from: {}".format(self.image, self.tag, self.registry_url) ): try: _, digest = self.client.manifest(self.image, self.tag, self.architecture, self.os) except DockerManifestError as e: self.log("Problem downloading manifest", detail=e.manifest) raise except (OSError, requests.RequestException) as e: raise SourceError(e) from e return self._digest_to_ref(digest) def is_resolved(self): return self.digest is not None def is_cached(self): mirror_dir = self.get_mirror_directory() try: manifest = self._load_manifest() for layer in manifest["layers"]: layer_digest = layer["digest"] blob_path = os.path.join(mirror_dir, layer_digest + ".tar.gz") try: self._verify_blob(blob_path, expected_digest=layer_digest) except FileNotFoundError: # digest fetched, but some layer blob not fetched return False return True except (FileNotFoundError, SourceError): return False def _load_manifest(self): manifest_file = os.path.join(self.get_mirror_directory(), self.digest + ".manifest.json") with open(manifest_file, "rb") as f: text = f.read() real_digest = self.client.digest(text) if real_digest != self.digest: raise SourceError("Manifest {} is corrupt; got content hash of {}".format(manifest_file, real_digest)) return json.loads(text.decode("utf-8")) def _save_manifest(self, text, path): manifest_file = os.path.join(path, self.digest + ".manifest.json") with save_file_atomic(manifest_file, "wb") as f: f.write(text.encode("utf-8")) @staticmethod def _verify_blob(path, expected_digest): blob_digest = "sha256:" + sha256sum(path) if expected_digest != blob_digest: raise SourceError("Blob {} is corrupt; got content hash of {}.".format(path, blob_digest)) def fetch(self): # pylint: disable=arguments-differ with self.timed_activity( "Fetching image {}:{} with digest {}".format(self.image, self.tag, self.digest), silent_nested=True, ): with self.tempdir() as tmpdir: # move all files to a tmpdir try: manifest = self._load_manifest() except FileNotFoundError as e: try: manifest_text, digest = self.client.manifest( self.image, self.digest, self.architecture, self.os, ) except requests.RequestException as ee: raise SourceError(ee) from ee if digest != self.digest: raise SourceError( "Requested image {}, got manifest with digest {}".format(self.digest, digest) ) from e self._save_manifest(manifest_text, tmpdir) manifest = json.loads(manifest_text) except DockerManifestError as e: self.log("Unexpected manifest", detail=e.manifest) raise except (OSError, requests.RequestException) as e: raise SourceError(e) from e for layer in manifest["layers"]: if layer["mediaType"] != "application/vnd.docker.image.rootfs.diff.tar.gzip": raise SourceError("Unsupported layer type: {}".format(layer["mediaType"])) layer_digest = layer["digest"] blob_path = os.path.join(tmpdir, layer_digest + ".tar.gz") if not os.path.exists(blob_path): try: self.client.blob(self.image, layer_digest, download_to=blob_path) except (OSError, requests.RequestException) as e: if os.path.exists(blob_path): shutil.rmtree(blob_path) raise SourceError(e) from e self._verify_blob(blob_path, expected_digest=layer_digest) # Only if all sources are successfully fetched, move files to staging directory # As both the manifest and blobs are content addressable, we can optimize space by having # a flat mirror directory. We check one-by-one if there is any need to copy a file out of the tmpdir. for fetched_file in os.listdir(tmpdir): move_atomic( os.path.join(tmpdir, fetched_file), os.path.join(self.get_mirror_directory(), fetched_file), ) def stage(self, directory): mirror_dir = self.get_mirror_directory() try: manifest = self._load_manifest() except (OSError, SourceError) as e: raise SourceError("Unable to load manifest: {}".format(e)) from e try: layer_members = [] # Create a list of members to extract from each layer for layer in manifest["layers"]: layer_digest = layer["digest"] blob_path = os.path.join(mirror_dir, layer_digest + ".tar.gz") self._verify_blob(blob_path, expected_digest=layer_digest) members_dict, whiteout_paths, opaque_whiteout_dirs = self._get_members_and_whiteout_paths(blob_path) # Process opaque whiteouts: remove members under the corresponding directories from previous layers for _, prev_members_dict in layer_members: members_to_remove = [] for member_path in prev_members_dict: # Check if this member should be removed by any opaque whiteout for opaque_whiteout_dir in opaque_whiteout_dirs: if member_path.startswith(opaque_whiteout_dir): members_to_remove.append(member_path) break for member_path in members_to_remove: del prev_members_dict[member_path] # Process whiteouts: remove corresponding members from previous layers for _, prev_members_dict in layer_members: for whiteout_path in whiteout_paths: prev_members_dict.pop(whiteout_path, None) layer_members.append((blob_path, members_dict)) # Extract files for each layer for blob_path, members_dict in layer_members: if not members_dict: # No files to extract from this layer continue # Extract files for the current layer with tarfile.open(blob_path, tarinfo=ReadableTarInfo) as tar: with self.tempdir() as td: members = list(members_dict.values()) if hasattr(tarfile, "tar_filter"): # Python 3.12+ (and older versions with backports) tar.extraction_filter = tarfile.tar_filter else: for member in members: self._assert_tarinfo_safe(member, td) tar.extractall(path=td, members=members) link_files(td, directory) except (OSError, SourceError, tarfile.TarError) as e: raise SourceError("{}: Error staging source: {}".format(self, e)) from e def collect_source_info(self): # If the image was configured with "url" only rather # than "registry-url" and "image", then self.image # at this point will have a leading forward slash "/". # # Lets just normalize that problem here, as changing # the self.image itself can break cache keys. # image_name = self.image.lstrip("/") return [ self.create_source_info( self.registry_url, SourceInfoMedium.OCI_IMAGE, SourceVersionType.OCI_DIGEST, self.digest, version_guess=self.version, extra_data={"image-name": image_name}, ) ] @staticmethod def _get_members_and_whiteout_paths(layer_tar_path): """Return the set of files to remove and extract for a given layer :param layer_tar_path: The path to the layer tar.gz file :return: Tuple of (members_dict, whiteout_paths, opaque_whiteout_dirs) - members_dict: OrderedDict of TarInfo members to extract into staging directory - whiteout_paths: list of file paths that should be removed from previous layers - opaque_whiteout_dirs: list of directory paths whose contents should be removed from previous layers """ def strip_wh(white_out_file): """Strip the prefixing .wh. for given file :param white_out_file: path of file :return: path without white-out prefix """ # whiteout files have the syntax of `*/.wh.*` file_name = os.path.basename(white_out_file) path = os.path.join(os.path.dirname(white_out_file), file_name.split(".wh.", maxsplit=1)[1]) return path def is_regular_file(info): """Check if file is a non-device file :param info: tar member metadata :return: if the file is a non-device file """ return not (info.name.startswith("dev/") or info.isdev()) with tarfile.open(layer_tar_path, tarinfo=ReadableTarInfo) as tar: members_dict = OrderedDict() whiteout_paths = [] opaque_whiteout_dirs = [] for member in tar.getmembers(): member_basename = os.path.basename(member.name) if member_basename == ".wh..wh..opq": # Opaque whiteout file opaque_whiteout_dir = os.path.dirname(member.name) if opaque_whiteout_dir != "": # Add "/" to facilitate path prefix matching opaque_whiteout_dir += "/" opaque_whiteout_dirs.append(opaque_whiteout_dir) elif member_basename.startswith(".wh."): # Whiteout file whiteout_paths.append(strip_wh(member.name)) elif is_regular_file(member): members_dict[member.name] = member return members_dict, whiteout_paths, opaque_whiteout_dirs # Assert that a tarfile is safe to extract; specifically, make # sure that we don't do anything outside of the target # directory (this is possible, if, say, someone engineered a # tarfile to contain paths that start with ..). def _assert_tarinfo_safe(self, member: tarfile.TarInfo, target_dir: str): final_path = os.path.abspath(os.path.join(target_dir, member.path)) if not final_path.startswith(target_dir): raise SourceError( "{}: Tarfile attempts to extract outside the staging area: " "{} -> {}".format(self, member.path, final_path) ) if member.islnk(): linked_path = os.path.abspath(os.path.join(target_dir, member.linkname)) if not linked_path.startswith(target_dir): raise SourceError( "{}: Tarfile attempts to hardlink outside the staging area: " "{} -> {}".format(self, member.path, final_path) ) # Don't need to worry about symlinks because they're just # files here and won't be able to do much harm once we are # in a sandbox. # Plugin entry point def setup(): return DockerSource buildstream-plugins-2.7.0/src/buildstream_plugins/sources/git.py000066400000000000000000001273551515254461100252160ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Authors: # Tristan Van Berkom # Chandan Singh # Tom Mewett # # This plugin was originally developped in the https://github.com/apache/buildstream/ # repository and was copied from 1a3c707a6c46573ab159de64ac9cd92e7f6027e6 # """ git - stage files from a git repository ======================================= **Host dependencies:** * git .. attention:: Note that this plugin **will checkout git submodules by default**; even if they are not specified in the `.bst` file. **Usage:** .. code:: yaml # Specify the git source kind kind: git # Specify the repository url, using an alias defined # in your project configuration is recommended. url: upstream:foo.git # Optionally specify a symbolic tracking branch or tag, this # will be used to update the 'ref' when refreshing the pipeline. track: master # Optionally specify the ref format used for tracking. # The default is 'sha1' for the raw commit hash. # If you specify 'git-describe', the commit hash will be prefixed # with the closest tag. ref-format: sha1 # Specify the commit ref, this must be specified in order to # checkout sources and build, but can be automatically updated # if the 'track' attribute was specified. ref: d63cbb6fdc0bbdadc4a1b92284826a6d63a7ebcd # Optionally specify whether submodules should be checked-out. # This is done recursively, as with `git clone --recurse-submodules`. # If not set, this will default to 'True' checkout-submodules: True # If your repository has submodules, explicitly specifying the # url from which they are to be fetched allows you to easily # rebuild the same sources from a different location. This is # especially handy when used with project defined aliases which # can be redefined at a later time, or overridden with mirrors. # # You may also explicitly specify whether to check out this # submodule. If 'checkout' is set, it will control whether to # checkout that submodule and recurse into it. It defaults to the # value of 'checkout-submodules'. submodules: plugins/bar: url: upstream:bar.git checkout: True plugins/bar/quux: checkout: False plugins/baz: url: upstream:baz.git checkout: False # Modify the default version guessing pattern # # Since 2.5 # version-guess-pattern: \'(\\d+)\\.(\\d+)(?:\\.(\\d+))?\' # Override the version guessing with an explicit version # # Since 2.5 # version: 5.9 # Enable tag tracking. # # This causes the `tags` metadata to be populated automatically # as a result of tracking the git source. # # By default this is 'False'. # track-tags: True # If the list of tags below is set, then a lightweight dummy # git repository will be staged along with the content at # build time. # # This is useful for a growing number of modules which use # `git describe` at build time in order to determine the version # which will be encoded into the built software. # # The 'tags' below is considered as a part of the git source # reference and will be stored in the 'project.refs' file if # that has been selected as your project's ref-storage. # # Migration notes: # # If you are upgrading from BuildStream 1.2, which used to # stage the entire repository by default, you will notice that # some modules which use `git describe` are broken, and will # need to enable this feature in order to fix them. # # If you need to enable this feature without changing the # the specific commit that you are building, then we recommend # the following migration steps for any git sources where # `git describe` is required: # # o Enable `track-tags` feature # o Set the `track` parameter to the desired commit sha which # the current `ref` points to # o Run `bst source track` for these elements, this will result in # populating the `tags` portion of the refs without changing # the refs # o Restore the `track` parameter to the branches which you have # previously been tracking afterwards. # tags: - tag: lightweight-example commit: 04ad0dc656cb7cc6feb781aa13bdbf1d67d0af78 annotated: false - tag: annotated-example commit: 10abe77fe8d77385d86f225b503d9185f4ef7f3a annotated: true See `built-in functionality doumentation `_ for details on common configuration options for sources. Configurable Warnings: ~~~~~~~~~~~~~~~~~~~~~~ This plugin provides the following `configurable warnings `_: - ``git:inconsistent-submodule`` - A submodule present in the git repository's .gitmodules was never added with `git submodule add`. - ``git:unlisted-submodule`` - A submodule is present in the git repository but was not specified in the source configuration and was not disabled for checkout. - ``git:invalid-submodule`` - A submodule is specified in the source configuration but does not exist in the repository. This plugin also utilises the following configurable `core warnings `_: - `ref-not-in-track `_ - The provided ref was not found in the provided track in the element's git repository. Reporting `SourceInfo `_ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The git source reports the URL of the git repository as the *url*. Further, the git source reports the `SourceInfoMedium.GIT `_ *medium* and the `SourceVersionType.COMMIT `_ *version_type*, for which it reports the git commit sha as the *version*. If the ref is found to be in ``git-describe`` format, an attempt to guess the version based on the git tag portion of the ref will be made for the reporting of the *guess_version*. Control over how the guess is made or overridden is controlled based on the ``version-guess-pattern`` and ``version`` configuration attributes described above. In order to understand how the ``version-guess-pattern`` works, please refer to the documentation for `utils.guess_version() `_ In the case that a git describe string represents a commit that is beyond the tag portion of the git describe reference (i.e. the version is not exact), then the number of commits found beyond the tag will be reported in the ``commit-offset`` field of the *extra_data*. .. attention:: SourceInfo is **not** reported for submodules. This is due to the limitation that the git plugin requires to have the toplevel module fetched first in order to have knowledge of the commits of the submodules. While this does not provide *complete* information on the provenance of sources, at least one can consider the module to be a comprehensive whole, and the versions of submodules are deterministically controlled by the version of the main repository. """ import os import re import shutil from io import StringIO from tempfile import TemporaryFile from configparser import RawConfigParser from buildstream import Source, SourceError, SourceFetcher from buildstream import CoreWarnings, FastEnum from buildstream import utils from buildstream.utils import DirectoryExistsError # # Soft import of buildstream symbols only available in newer versions # # The BST_MIN_VERSION will provide a better user experience. # try: from buildstream import SourceInfoMedium, SourceVersionType except ImportError: pass GIT_MODULES = ".gitmodules" EXACT_TAG_PATTERN = r"(?P.*)-0-g(?P.*)" # Warnings WARN_INCONSISTENT_SUBMODULE = "inconsistent-submodule" WARN_UNLISTED_SUBMODULE = "unlisted-submodule" WARN_INVALID_SUBMODULE = "invalid-submodule" class _RefFormat(FastEnum): SHA1 = "sha1" GIT_DESCRIBE = "git-describe" def _strip_tag(rev): return rev.split("-g")[-1] # This class represents a single Git repository. The Git source needs to account for # submodules, but we don't want to cache them all under the umbrella of the # superproject - so we use this class which caches them independently, according # to their URL. Instances keep reference to their "parent" GitSource, # and if applicable, where in the superproject they are found. # # Args: # source (GitSource): The parent source # path (str): The relative location of the submodule in the superproject; # the empty string for the superproject itself # url (str): Where to clone the repo from # ref (str): Specified 'ref' from the source configuration # primary (bool): Whether this is the primary URL for the source # tags (list): Tag configuration; see GitSource._load_tags # class GitMirror(SourceFetcher): def __init__(self, source, path, url, ref, *, primary=False, tags=None): super().__init__() self.source = source self.path = path self.url = url self.ref = ref self.tags = tags or [] self.primary = primary self.mirror = os.path.join(source.get_mirror_directory(), utils.url_directory_name(url)) # _ensure_repo(): # # Ensures that the Git repository exists at the mirror location and is configured # to fetch from the given URL # def _ensure_repo(self): if not os.path.exists(self.mirror): with self.source.tempdir() as tmpdir: self.source.call( [self.source.host_git, "init", "--bare", tmpdir], fail="Failed to initialise repository", ) try: utils.move_atomic(tmpdir, self.mirror) except DirectoryExistsError: # Another process was quicker to download this repository. # Let's discard our own self.source.status("{}: Discarding duplicate repository".format(self.source)) except OSError as e: raise SourceError( "{}: Failed to move created repository from '{}' to '{}': {}".format( self.source, tmpdir, self.mirror, e ) ) from e def _fetch(self, url, fetch_all=False): self._ensure_repo() # Work out whether we can fetch a specific tag: are we given a ref which # 1. is in git-describe format # 2. refers to an exact tag (is "...-0-g...") # 3. is available on the remote and tags the specified commit? # And lastly: are we on a new-enough Git which allows cloning from our potentially shallow cache? if fetch_all: pass # Fetching from a shallow-cloned repo was first supported in v1.9.0 elif not self.ref or self.source.host_git_version is not None and self.source.host_git_version < (1, 9, 0): fetch_all = True else: m = re.match(EXACT_TAG_PATTERN, self.ref) if m is None: fetch_all = True else: tag = m.group("tag") commit = m.group("commit") if not self.remote_has_tag(url, tag, commit): self.source.status( "{}: {} is not advertised on {}. Fetching all Git refs".format(self.source, self.ref, url) ) fetch_all = True else: exit_code = self.source.call( [ self.source.host_git, "fetch", "--depth=1", url, "+refs/tags/{tag}:refs/tags/{tag}".format(tag=tag), ], cwd=self.mirror, ) if exit_code != 0: self.source.status( "{}: Failed to fetch tag '{}' from {}. Fetching all Git refs".format(self.source, tag, url) ) fetch_all = True if fetch_all: self.source.call( [ self.source.host_git, "fetch", "--prune", url, "+refs/heads/*:refs/heads/*", "+refs/tags/*:refs/tags/*", ], fail="Failed to fetch from remote git repository: {}".format(url), fail_temporarily=True, cwd=self.mirror, ) def fetch(self, alias_override=None): # pylint: disable=arguments-differ resolved_url = self.source.translate_url(self.url, alias_override=alias_override, primary=self.primary) with self.source.timed_activity("Fetching from {}".format(resolved_url), silent_nested=True): if not self.has_ref(): self._fetch(resolved_url) self.assert_ref() def has_ref(self): if not self.ref: return False # If the mirror doesnt exist, we also dont have the ref if not os.path.exists(self.mirror): return False # Check if the ref is really there rc = self.source.call([self.source.host_git, "cat-file", "-t", self.ref], cwd=self.mirror) return rc == 0 def assert_ref(self): if not self.has_ref(): raise SourceError( "{}: expected ref '{}' was not found in git repository: '{}'".format(self.source, self.ref, self.url) ) # remote_has_tag(): # # Args: # url (str) # tag (str) # commit (str) # # Returns: # (bool): Whether the remote at `url` has the tag `tag` attached to `commit` # def remote_has_tag(self, url, tag, commit): _, ls_remote = self.source.check_output( [self.source.host_git, "ls-remote", url], cwd=self.mirror, fail="Failed to list advertised remote refs from git repository {}".format(url), ) line = "{commit}\trefs/tags/{tag}".format(commit=commit, tag=tag) return line in ls_remote or line + "^{}" in ls_remote # to_commit(): # # Args: # rev (str): A Git "commit-ish" rev # # Returns: # (str): The full revision ID of the commit # def to_commit(self, rev): _, output = self.source.check_output( [self.source.host_git, "rev-list", "-n", "1", rev], fail="Unable to find revision {}".format(rev), cwd=self.mirror, ) return output.strip() # describe(): # # Args: # rev (str): A Git "commit-ish" rev # # Returns: # (str): The full revision ID of the commit given by rev, prepended # with tag information as given by git-describe (where available) # def describe(self, rev): _, output = self.source.check_output( [ self.source.host_git, "describe", "--tags", "--abbrev=40", "--long", "--always", rev, ], fail="Unable to find revision {}".format(rev), cwd=self.mirror, ) return output.strip() # reachable_tags(): # # Args: # rev (str): A Git "commit-ish" rev # # Returns: # (list): A list of tags in the ancestry of rev. Each entry is a triple of the form # (tag name (str), commit ref (str), annotated (bool)) describing a tag, # its tagged commit and whether it's annotated # def reachable_tags(self, rev): tags = set() for options in [ [], ["--first-parent"], ["--tags"], ["--tags", "--first-parent"], ]: exit_code, output = self.source.check_output( [ self.source.host_git, "describe", "--abbrev=0", rev, *options, ], cwd=self.mirror, ) if exit_code == 0: tag = output.strip() _, commit_ref = self.source.check_output( [self.source.host_git, "rev-parse", tag + "^{commit}"], fail="Unable to resolve tag '{}'".format(tag), cwd=self.mirror, ) exit_code = self.source.call( [self.source.host_git, "cat-file", "tag", tag], cwd=self.mirror, ) annotated = exit_code == 0 tags.add((tag, commit_ref.strip(), annotated)) return list(tags) def stage(self, directory): fullpath = os.path.join(directory, self.path) # Using --shared here avoids copying the objects into the checkout, in any # case we're just checking out a specific commit and then removing the .git/ # directory. self.source.call( [ self.source.host_git, "clone", "--no-checkout", "--shared", self.mirror, fullpath, ], fail="Failed to create git mirror {} in directory: {}".format(self.mirror, fullpath), fail_temporarily=True, ) self.source.call( [self.source.host_git, "checkout", "--force", self.ref], fail="Failed to checkout git ref {}".format(self.ref), cwd=fullpath, ) # Remove .git dir shutil.rmtree(os.path.join(fullpath, ".git")) self._rebuild_git(fullpath) def init_workspace(self, directory): fullpath = os.path.join(directory, self.path) url = self.source.translate_url(self.url) self.source.call( [ self.source.host_git, "clone", "--no-checkout", self.mirror, fullpath, ], fail="Failed to clone git mirror {} in directory: {}".format(self.mirror, fullpath), fail_temporarily=True, ) self.source.call( [self.source.host_git, "remote", "set-url", "origin", url], fail='Failed to add remote origin "{}"'.format(url), cwd=fullpath, ) self.source.call( [self.source.host_git, "checkout", "--force", self.ref], fail="Failed to checkout git ref {}".format(self.ref), cwd=fullpath, ) # get_submodule_mirrors(): # # Returns: # An iterator through new instances of this class, one of each submodule # in the repo # def get_submodule_mirrors(self): for path, url in self.submodule_list(): ref = self.submodule_ref(path) if ref is not None: mirror = self.__class__(self.source, os.path.join(self.path, path), url, ref) yield mirror # List the submodules (path/url tuples) present at the given ref of this repo def submodule_list(self): modules = "{}:{}".format(self.ref, GIT_MODULES) exit_code, output = self.source.check_output([self.source.host_git, "show", modules], cwd=self.mirror) # If git show reports error code 128 here, we take it to mean there is # no .gitmodules file to display for the given revision. if exit_code == 128: return elif exit_code != 0: raise SourceError("{plugin}: Failed to show gitmodules at ref {ref}".format(plugin=self, ref=self.ref)) content = "\n".join([l.strip() for l in output.splitlines()]) io = StringIO(content) parser = RawConfigParser() parser.read_file(io) for section in parser.sections(): # validate section name against the 'submodule "foo"' pattern if re.match(r'submodule "(.*)"', section): path = parser.get(section, "path") url = parser.get(section, "url") yield (path, url) # Fetch the ref which this mirror requires its submodule to have, # at the given ref of this mirror. def submodule_ref(self, submodule, ref=None): if not ref: ref = self.ref # list objects in the parent repo tree to find the commit # object that corresponds to the submodule _, output = self.source.check_output( [self.source.host_git, "ls-tree", ref, submodule], fail="ls-tree failed for commit {} and submodule: {}".format(ref, submodule), cwd=self.mirror, ) # read the commit hash from the output fields = output.split() if len(fields) >= 2 and fields[1] == "commit": submodule_commit = output.split()[2] # fail if the commit hash is invalid if len(submodule_commit) != 40: raise SourceError( "{}: Error reading commit information for submodule '{}'".format(self.source, submodule) ) return submodule_commit else: detail = ( "The submodule '{}' is defined either in the BuildStream source\n".format(submodule) + "definition, or in a .gitmodules file. But the submodule was never added to the\n" + "underlying git repository with `git submodule add`." ) self.source.warn( "{}: Ignoring inconsistent submodule '{}'".format(self.source, submodule), detail=detail, warning_token=WARN_INCONSISTENT_SUBMODULE, ) return None def _rebuild_git(self, fullpath): if not self.tags: return with self.source.tempdir() as tmpdir: included = set() shallow = set() for _, commit_ref, _ in self.tags: if commit_ref == self.ref: # rev-list does not work in case of same rev shallow.add(self.ref) else: _, out = self.source.check_output( [ self.source.host_git, "rev-list", "--ancestry-path", "--boundary", "{}..{}".format(commit_ref, self.ref), ], fail="Failed to get git history {}..{} in directory: {}".format( commit_ref, self.ref, fullpath ), fail_temporarily=True, cwd=self.mirror, ) self.source.warn("refs {}..{}: {}".format(commit_ref, self.ref, out.splitlines())) for line in out.splitlines(): rev = line.lstrip("-") if line[0] == "-": shallow.add(rev) else: included.add(rev) shallow -= included included |= shallow self.source.call( [self.source.host_git, "init"], fail="Cannot initialize git repository: {}".format(fullpath), cwd=fullpath, ) for rev in included: with TemporaryFile(dir=tmpdir) as commit_file: self.source.call( [self.source.host_git, "cat-file", "commit", rev], stdout=commit_file, fail="Failed to get commit {}".format(rev), cwd=self.mirror, ) commit_file.seek(0, 0) self.source.call( [ self.source.host_git, "hash-object", "-w", "-t", "commit", "--stdin", ], stdin=commit_file, fail="Failed to add commit object {}".format(rev), cwd=fullpath, ) with open( os.path.join(fullpath, ".git", "shallow"), "w", encoding="utf-8", ) as shallow_file: for rev in shallow: shallow_file.write("{}\n".format(rev)) for tag, commit_ref, annotated in self.tags: if annotated: with TemporaryFile(dir=tmpdir) as tag_file: tag_data = "object {}\ntype commit\ntag {}\ntagger Unspecified Tagger 0 +0000\n".format( commit_ref, tag ) tag_file.write(tag_data.encode("ascii")) tag_file.seek(0, 0) _, tag_ref = self.source.check_output( [ self.source.host_git, "hash-object", "-w", "-t", "tag", "--stdin", ], stdin=tag_file, fail="Failed to add tag object {}".format(tag), cwd=fullpath, ) self.source.call( [self.source.host_git, "tag", tag, tag_ref.strip()], fail="Failed to tag: {}".format(tag), cwd=fullpath, ) else: self.source.call( [self.source.host_git, "tag", tag, commit_ref], fail="Failed to tag: {}".format(tag), cwd=fullpath, ) with open(os.path.join(fullpath, ".git", "HEAD"), "w", encoding="utf-8") as head: self.source.call( [self.source.host_git, "rev-parse", self.ref], stdout=head, fail="Failed to parse commit {}".format(self.ref), cwd=self.mirror, ) class GitSource(Source): # pylint: disable=attribute-defined-outside-init BST_MIN_VERSION = "2.5" def configure(self, node): ref = node.get_str("ref", None) config_keys = [ "url", "track", "ref", "submodules", "checkout-submodules", "ref-format", "track-tags", "tags", "version", "version-guess-pattern", ] node.validate_keys(config_keys + Source.COMMON_CONFIG_KEYS) tags_node = node.get_sequence("tags", []) for tag_node in tags_node: tag_node.validate_keys(["tag", "commit", "annotated"]) tags = self._load_tags(node) self.track_tags = node.get_bool("track-tags", default=False) self.original_url = node.get_str("url") self.mirror = GitMirror(self, "", self.original_url, ref, tags=tags, primary=True) self.tracking = node.get_str("track", None) self.ref_format = node.get_enum("ref-format", _RefFormat, _RefFormat.SHA1) self.guess_pattern_string = node.get_str("version-guess-pattern", None) self.guess_pattern = None if self.guess_pattern_string is not None: self.guess_pattern = re.compile(self.guess_pattern_string) # Get the explicitly set guess_version for the toplevel git repo self.version = node.get_str("version", None) # At this point we now know if the source has a ref and/or a track. # If it is missing both then we will be unable to track or build. if self.mirror.ref is None and self.tracking is None: raise SourceError( "{}: Git sources require a ref and/or track".format(self), reason="missing-track-and-ref", ) self.checkout_submodules = node.get_bool("checkout-submodules", default=True) # Parse a dict of submodule overrides, stored in the submodule_overrides # and submodule_checkout_overrides dictionaries. self.submodule_overrides = {} self.submodule_checkout_overrides = {} modules = node.get_mapping("submodules", {}) for path in modules.keys(): submodule = modules.get_mapping(path) url = submodule.get_str("url", None) # Make sure to mark all URLs that are specified in the configuration if url: self.mark_download_url(url, primary=False) self.submodule_overrides[path] = url if "checkout" in submodule: checkout = submodule.get_bool("checkout") self.submodule_checkout_overrides[path] = checkout self.mark_download_url(self.original_url) def preflight(self): # Check if git is installed, get the binary at the same time self.host_git = utils.get_host_tool("git") rc, version_str = self.check_output([self.host_git, "--version"]) # e.g. on Git for Windows we get "git version 2.21.0.windows.1". # e.g. on Mac via Homebrew we get "git version 2.19.0". if rc == 0: self.host_git_version = tuple(int(x) for x in version_str.split(" ")[2].split(".")[:3]) else: self.host_git_version = None def get_unique_key(self): ref = self.mirror.ref if ref is not None: # Strip any (arbitary) tag information, leaving just the commit ID ref = _strip_tag(ref) # Here we want to encode the local name of the repository and # the ref, if the user changes the alias to fetch the same sources # from another location, it should not affect the cache key. key = [self.original_url, ref] if self.mirror.tags: tags = {tag: (commit, annotated) for tag, commit, annotated in self.mirror.tags} key.append({"tags": tags}) # Only modify the cache key with checkout_submodules if it's something # other than the default behaviour. if self.checkout_submodules is False: key.append({"checkout_submodules": self.checkout_submodules}) # We want the cache key to change if the source was # configured differently, and submodules count. if self.submodule_overrides: key.append(self.submodule_overrides) if self.submodule_checkout_overrides: key.append({"submodule_checkout_overrides": self.submodule_checkout_overrides}) # Backwards compatible method of supporting configuration # attributes which affect SourceInfo generation. if self.version is not None: key.append(self.version) elif self.guess_pattern_string is not None: key.append(self.guess_pattern_string) return key def is_resolved(self): return self.mirror.ref is not None def is_cached(self): return self._have_all_refs() def load_ref(self, node): self.mirror.ref = node.get_str("ref", None) self.mirror.tags = self._load_tags(node) def get_ref(self): if self.mirror.ref is None: return None return self.mirror.ref, self.mirror.tags def set_ref(self, ref, node): if not ref: self.mirror.ref = None if "ref" in node: del node["ref"] self.mirror.tags = [] if "tags" in node: del node["tags"] else: actual_ref, tags = ref node["ref"] = self.mirror.ref = actual_ref self.mirror.tags = tags if tags: node["tags"] = [ { "tag": tag, "commit": commit_ref, "annotated": annotated, } for tag, commit_ref, annotated in tags ] else: if "tags" in node: del node["tags"] def track(self): # pylint: disable=arguments-differ # If self.tracking is not specified it's not an error, just silently return if not self.tracking: # Is there a better way to check if a ref is given. if self.mirror.ref is None: detail = "Without a tracking branch ref can not be updated. Please " + "provide a ref or a track." raise SourceError( "{}: No track or ref".format(self), detail=detail, reason="track-attempt-no-track", ) return None # Resolve the URL for the message resolved_url = self.translate_url(self.mirror.url) with self.timed_activity( "Tracking {} from {}".format(self.tracking, resolved_url), silent_nested=True, ): self.mirror._fetch(resolved_url, fetch_all=True) ref = self.mirror.to_commit(self.tracking) tags = self.mirror.reachable_tags(ref) if self.track_tags else [] if self.ref_format == _RefFormat.GIT_DESCRIBE: ref = self.mirror.describe(ref) return ref, tags def init_workspace(self, directory): with self.timed_activity('Setting up workspace "{}"'.format(directory), silent_nested=True): self.mirror.init_workspace(directory) for mirror in self._recurse_submodules(configure=True): mirror.init_workspace(directory) def stage(self, directory): # Stage the main repo in the specified directory # with self.timed_activity("Staging {}".format(self.mirror.url), silent_nested=True): self.mirror.stage(directory) for mirror in self._recurse_submodules(configure=True): mirror.stage(directory) def get_source_fetchers(self): self.mirror.mark_download_url(self.mirror.url) yield self.mirror # _recurse_submodules only iterates those which are known at the current # cached state - but fetch is called on each result as we go, so this will # yield all configured submodules for submodule in self._recurse_submodules(configure=True): submodule.mark_download_url(submodule.url) yield submodule def validate_cache(self): discovered_submodules = {} unlisted_submodules = [] invalid_submodules = [] for submodule in self._recurse_submodules(configure=False): discovered_submodules[submodule.path] = submodule.url if self._ignoring_submodule(submodule.path): continue if submodule.path not in self.submodule_overrides: unlisted_submodules.append((submodule.path, submodule.url)) # Warn about submodules which are explicitly configured but do not exist for path, url in self.submodule_overrides.items(): if path not in discovered_submodules: invalid_submodules.append((path, url)) if invalid_submodules: detail = [] for path, url in invalid_submodules: detail.append(" Submodule URL '{}' at path '{}'".format(url, path)) self.warn( "{}: Invalid submodules specified".format(self), warning_token=WARN_INVALID_SUBMODULE, detail="The following submodules are specified in the source " "description but do not exist according to the repository\n\n" + "\n".join(detail), ) # Warn about submodules which exist but have not been explicitly configured if unlisted_submodules: detail = [] for path, url in unlisted_submodules: detail.append(" Submodule URL '{}' at path '{}'".format(url, path)) self.warn( "{}: Unlisted submodules exist".format(self), warning_token=WARN_UNLISTED_SUBMODULE, detail="The following submodules exist but are not specified " + "in the source description\n\n" + "\n".join(detail), ) # Assert that the ref exists in the track tag/branch, if track has been specified. # Also don't do this check if an exact tag ref is given, as we probably didn't fetch # any branch refs ref_in_track = False if not re.match(EXACT_TAG_PATTERN, self.mirror.ref) and self.tracking: _, branch = self.check_output( [ self.host_git, "branch", "--list", self.tracking, "--contains", self.mirror.ref, ], cwd=self.mirror.mirror, ) if branch: ref_in_track = True else: _, tag = self.check_output( [ self.host_git, "tag", "--list", self.tracking, "--contains", self.mirror.ref, ], cwd=self.mirror.mirror, ) if tag: ref_in_track = True if not ref_in_track: detail = ( "The ref provided for the element does not exist locally " + "in the provided track branch / tag '{}'.\n".format(self.tracking) + "You may wish to track the element to update the ref from '{}' ".format(self.tracking) + "with `bst source track`,\n" + "or examine the upstream at '{}' for the specific ref.".format(self.mirror.url) ) self.warn( "{}: expected ref '{}' was not found in given track '{}' for staged repository: '{}'\n".format( self, self.mirror.ref, self.tracking, self.mirror.url ), detail=detail, warning_token=CoreWarnings.REF_NOT_IN_TRACK, ) # _guess_version() # # Guess the version, in the case the ref is recorded in git-describe format # # Returns: a three-tuple composed of the following three values: # - the version (a git sha) # - the version_guess (as guessed from the tag portion of a git describe string) # - the number of commits beyond the tag (indicating that the guessed version is in this case inexact) # # All returns are either None or strings, only the "sha" is guaranteed to be discovered. # def _guess_version(self): version_guess = self.version commits = None commit_sha = None string = self.mirror.ref main_split = string.rsplit("-g", 1) if len(main_split) < 2: # Didn't find the `-g`, assume its a raw git sha as input commit_sha = string else: # Split out the commits and the tag portion (which may also contain '-') commit_sha = main_split[1] sub_split = main_split[0].rsplit("-", 1) commits = sub_split[1] if commits == "0": commits = None # Run the guess logic on the tag portion of the git describe string if version_guess is None: version_guess = utils.guess_version(sub_split[0], pattern=self.guess_pattern) return (commit_sha, version_guess, commits) def collect_source_info(self): extra_data = None commit_sha, version_guess, commits = self._guess_version() if commits: extra_data = {"commit-offset": commits} return [ self.create_source_info( self.mirror.url, SourceInfoMedium.GIT, SourceVersionType.COMMIT, commit_sha, version_guess=version_guess, extra_data=extra_data, ) ] ########################################################### # Local Functions # ########################################################### def _have_all_refs(self): return self.mirror.has_ref() and all( submodule.has_ref() for submodule in self._recurse_submodules(configure=True) ) # _configure_submodules(): # # Args: # submodules: An iterator of GitMirror (or similar) objects for submodules # # Returns: # An iterator through `submodules` but filtered of any ignored submodules # and modified to use any custom URLs configured in the source # def _configure_submodules(self, submodules): for submodule in submodules: if self._ignoring_submodule(submodule.path): continue # Allow configuration to override the upstream location of the submodules. submodule.url = self.submodule_overrides.get(submodule.path, submodule.url) yield submodule # _recurse_submodules(): # # Recursively iterates through GitMirrors for submodules of the main repo. Only # submodules that are cached are recursed into - but this is decided at # iteration time, so you can fetch in a for loop over this function to fetch # all submodules. # # Args: # configure (bool): Whether to apply the 'submodule' config while recursing # (URL changing and 'checkout' overrides) # def _recurse_submodules(self, configure): def recurse(mirror): submodules = mirror.get_submodule_mirrors() if configure: submodules = self._configure_submodules(submodules) for submodule in submodules: yield submodule if submodule.has_ref(): yield from recurse(submodule) yield from recurse(self.mirror) def _load_tags(self, node): tags = [] tags_node = node.get_sequence("tags", []) for tag_node in tags_node: tag = tag_node.get_str("tag") commit_ref = tag_node.get_str("commit") annotated = tag_node.get_bool("annotated") tags.append((tag, commit_ref, annotated)) return tags # _ignoring_submodule(): # # Args: # path (str): The path of a submodule in the superproject # # Returns: # (bool): Whether to not clone/checkout this submodule # def _ignoring_submodule(self, path): return not self.submodule_checkout_overrides.get(path, self.checkout_submodules) # Plugin entry point def setup(): return GitSource buildstream-plugins-2.7.0/src/buildstream_plugins/sources/patch.py000066400000000000000000000113321515254461100255150ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Authors: # Chandan Singh # Tiago Gomes # # This plugin was originally developped in the https://github.com/apache/buildstream/ # repository and was copied from 1a3c707a6c46573ab159de64ac9cd92e7f6027e6 # """ patch - apply locally stored patches ==================================== **Host dependencies:** * patch **Usage:** .. code:: yaml # Specify the local source kind kind: patch # Specify the project relative path to a patch file path: files/somefile.diff # Optionally specify the strip level, defaults to 1 strip-level: 1 See `built-in functionality doumentation `_ for details on common configuration options for sources. Reporting `SourceInfo `_ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The patch source reports the project relative path of the patch file as the *url*. Further, the patch source reports the `SourceInfoMedium.LOCAL `_ *medium* and the `SourceVersionType.SHA256 `_ *version_type*, for which it reports the sha256 checksum of the patch file as the *version*. The *guess_version* of a patch source is meaningless, as it is tied instead to the BuildStream project in which it is contained. """ import os from buildstream import Source, SourceError from buildstream import utils # # Soft import of buildstream symbols only available in newer versions # # The BST_MIN_VERSION will provide a better user experience. # try: from buildstream import SourceInfoMedium, SourceVersionType except ImportError: pass class PatchSource(Source): # pylint: disable=attribute-defined-outside-init BST_MIN_VERSION = "2.5" BST_REQUIRES_PREVIOUS_SOURCES_STAGE = True def configure(self, node): node.validate_keys(["path", "strip-level", *Source.COMMON_CONFIG_KEYS]) self.path = self.node_get_project_path(node.get_scalar("path"), check_is_file=True) self.strip_level = node.get_int("strip-level", default=1) self.fullpath = os.path.join(self.get_project_directory(), self.path) self.sha256 = None def preflight(self): # Check if patch is installed, get the binary at the same time self.host_patch = utils.get_host_tool("patch") def get_unique_key(self): self.sha256 = utils.sha256sum(self.fullpath) return [self.path, self.sha256, self.strip_level] def is_resolved(self): return True def is_cached(self): return True def load_ref(self, node): pass def get_ref(self): return None # pragma: nocover def set_ref(self, ref, node): pass # pragma: nocover def fetch(self): # pylint: disable=arguments-differ # Nothing to do here for a local source pass # pragma: nocover def stage(self, directory): with self.timed_activity("Applying local patch: {}".format(self.path)): # Bail out with a comprehensive message if the target directory is empty if not os.listdir(directory): raise SourceError( "Nothing to patch in directory '{}'".format(directory), reason="patch-no-files", ) strip_level_option = "-p{}".format(self.strip_level) self.call( [ self.host_patch, strip_level_option, "-i", self.fullpath, "-d", directory, ], fail="Failed to apply patch {}".format(self.path), ) def collect_source_info(self): return [self.create_source_info(self.path, SourceInfoMedium.LOCAL, SourceVersionType.SHA256, self.sha256)] # Plugin entry point def setup(): return PatchSource buildstream-plugins-2.7.0/src/buildstream_plugins/sources/pip.py000066400000000000000000000257621515254461100252220ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Authors: # Chandan Singh # # This plugin was originally developped in the https://github.com/apache/buildstream/ # repository and was copied from 1a3c707a6c46573ab159de64ac9cd92e7f6027e6 # """ pip - stage python packages using pip ===================================== **Host depndencies:** * ``pip`` python module This plugin will download source distributions for specified packages using ``pip`` but will not install them. It is expected that the elements using this source will install the downloaded packages. Downloaded tarballs will be stored in a directory called ".bst_pip_downloads". **Usage:** .. code:: yaml # Specify the pip source kind kind: pip # Optionally specify index url, defaults to PyPi # This url is used to discover new versions of packages and download them # Projects intending to mirror their sources to a permanent location should # use an aliased url, and declare the alias in the project configuration url: https://mypypi.example.com/simple # Optionally specify the path to requirements files # Note that either 'requirements-files' or 'packages' must be defined requirements-files: - requirements.txt # Optionally specify a list of additional packages # Note that either 'requirements-files' or 'packages' must be defined packages: - flake8 # Specify the ref. It is a list of strings of format # "==", separated by "\\n". # Usually this will be contents of a requirements.txt file where all # package versions have been frozen. ref: "flake8==3.5.0\\nmccabe==0.6.1\\npkg-resources==0.0.0\\npycodestyle==2.3.1\\npyflakes==1.6.0" See `built-in functionality doumentation `_ for details on common configuration options for sources. Reporting `SourceInfo `_ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The pip source reports the python package index (PyPI) instance as the *url*. Further, the pip source reports the `SourceInfoMedium.PYTHON_PACKAGE_INDEX `_ *medium* and the `SourceVersionType.INDEXED_VERSION `_ *version_type*, for which it reports the tracked package version as the *version* and the *version_guess*. Additionally, the pip source reports the package name through the ``package-name`` key of the *extra_data*. The pip source will report one SourceInfo instance for each of the packages discovered in tracking. """ import hashlib import os import re from buildstream import Source, SourceError from buildstream import utils # # Soft import of buildstream symbols only available in newer versions # # The BST_MIN_VERSION will provide a better user experience. # try: from buildstream import SourceInfoMedium, SourceVersionType except ImportError: pass _OUTPUT_DIRNAME = ".bst_pip_downloads" _PYPI_INDEX_URL = "https://pypi.org/simple/" # Used only for finding pip command _PYTHON_VERSIONS = [ "python", # when running in a venv, we might not have the exact version "python2.7", "python3.0", "python3.1", "python3.2", "python3.3", "python3.4", "python3.5", "python3.6", "python3.7", "python3.8", "python3.9", "python3.10", "python3.11", "python3.12", "python3", ] # List of allowed extensions taken from # https://docs.python.org/3/distutils/sourcedist.html. # Names of source distribution archives must be of the form # '%{package-name}-%{version}.%{extension}'. _SDIST_RE = re.compile( r"^([\w.-]+?)-((?:[\d.]+){2,})\.(?:tar|tar.bz2|tar.gz|tar.xz|tar.Z|zip)$", re.IGNORECASE, ) class PipSource(Source): # pylint: disable=attribute-defined-outside-init BST_MIN_VERSION = "2.5" # We need access to previous sources at track time to use requirements.txt # but not at fetch time as self.ref should contain sufficient information # for this plugin BST_REQUIRES_PREVIOUS_SOURCES_TRACK = True def configure(self, node): node.validate_keys(["url", "packages", "ref", "requirements-files"] + Source.COMMON_CONFIG_KEYS) self.ref = node.get_str("ref", None) self.original_url = node.get_str("url", _PYPI_INDEX_URL) self.index_url = self.translate_url(self.original_url) self.packages = node.get_str_list("packages", []) self.requirements_files = node.get_str_list("requirements-files", []) if not (self.packages or self.requirements_files): raise SourceError("{}: Either 'packages' or 'requirements-files' must be specified".format(self)) def preflight(self): # Try to find a pip version that spports download command self.host_pip = None for python in reversed(_PYTHON_VERSIONS): try: host_python = utils.get_host_tool(python) rc = self.call([host_python, "-m", "pip", "download", "--help"]) if rc == 0: self.host_pip = [host_python, "-m", "pip"] break except utils.ProgramNotFoundError: pass if self.host_pip is None: raise SourceError("{}: Unable to find a suitable pip command".format(self)) def get_unique_key(self): return [self.original_url, self.ref] def is_cached(self): return os.path.exists(self._mirror) and os.listdir(self._mirror) def get_ref(self): return self.ref def load_ref(self, node): self.ref = node.get_str("ref", None) def set_ref(self, ref, node): node["ref"] = self.ref = ref def track(self, previous_sources_dir): # pylint: disable=arguments-differ # XXX pip does not offer any public API other than the CLI tool so it # is not feasible to correctly parse the requirements file or to check # which package versions pip is going to install. # See https://pip.pypa.io/en/stable/user_guide/#using-pip-from-your-program # for details. # As a result, we have to wastefully install the packages during track. with self.tempdir() as tmpdir: install_args = self.host_pip + [ "download", "--no-binary", ":all:", "--index-url", self.index_url, "--dest", tmpdir, ] for requirement_file in self.requirements_files: fpath = os.path.join(previous_sources_dir, requirement_file) install_args += ["-r", fpath] install_args += self.packages self.call(install_args, fail="Failed to install python packages") reqs = self._parse_sdist_names(tmpdir) return "\n".join(["{}=={}".format(pkg, ver) for pkg, ver in reqs]) def fetch(self): # pylint: disable=arguments-differ with self.tempdir() as tmpdir: packages = self.ref.strip().split("\n") package_dir = os.path.join(tmpdir, "packages") os.makedirs(package_dir) self.call( [ *self.host_pip, "download", "--no-binary", ":all:", "--index-url", self.index_url, "--dest", package_dir, *packages, ], fail="Failed to install python packages: {}".format(packages), ) # If the mirror directory already exists, assume that some other # process has fetched the sources before us and ensure that we do # not raise an error in that case. try: utils.move_atomic(package_dir, self._mirror) except utils.DirectoryExistsError: # Another process has beaten us and has fetched the sources # before us. pass except OSError as e: raise SourceError( "{}: Failed to move downloaded pip packages from '{}' to '{}': {}".format( self, package_dir, self._mirror, e ) ) from e def stage(self, directory): with self.timed_activity("Staging Python packages", silent_nested=True): utils.copy_files(self._mirror, os.path.join(directory, _OUTPUT_DIRNAME)) def collect_source_info(self): infos = [] packages_versions = self.ref.splitlines() for package_version in packages_versions: split = package_version.split("==") package = split[0] version = split[1] infos.append( self.create_source_info( self.index_url, SourceInfoMedium.PYTHON_PACKAGE_INDEX, SourceVersionType.INDEXED_VERSION, version, version_guess=version, extra_data={"package-name": package}, ) ) return infos # Directory where this source should stage its files # @property def _mirror(self): if not self.ref: return None return os.path.join( self.get_mirror_directory(), utils.url_directory_name(self.original_url), hashlib.sha256(self.ref.encode()).hexdigest(), ) # Parse names of downloaded source distributions # # Args: # basedir (str): Directory containing source distribution archives # # Returns: # (list): List of (package_name, version) tuples in sorted order # def _parse_sdist_names(self, basedir): reqs = [] for f in os.listdir(basedir): pkg = _match_package_name(f) if pkg is not None: reqs.append(pkg) return sorted(reqs) # Extract the package name and version of a source distribution # # Args: # filename (str): Filename of the source distribution # # Returns: # (tuple): A tuple of (package_name, version) # def _match_package_name(filename): pkg_match = _SDIST_RE.match(filename) if pkg_match is None: return None return pkg_match.groups() def setup(): return PipSource buildstream-plugins-2.7.0/tests/000077500000000000000000000000001515254461100166605ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/__init__.py000066400000000000000000000000001515254461100207570ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/bzr_wrapper000077500000000000000000000002451515254461100211440ustar00rootroot00000000000000#!/bin/bash if [ -x "/usr/bin/bzr" ]; then cat > "${1}/bzr" << 'EOF' #!/bin/bash export PATH=/usr/bin exec /usr/bin/bzr "$@" EOF chmod +x "${1}/bzr" fi buildstream-plugins-2.7.0/tests/cachekey/000077500000000000000000000000001515254461100204345ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/cachekey/__init__.py000066400000000000000000000000001515254461100225330ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/cachekey/cachekey.py000066400000000000000000000030431515254461100225620ustar00rootroot00000000000000# Pylint doesn't play well with fixtures and dependency injection from pytest # pylint: disable=redefined-outer-name import os import pytest from buildstream._testing._cachekeys import check_cache_key_stability from buildstream._testing.runcli import cli # pylint: disable=unused-import from buildstream._testing._utils.site import HAVE_BZR, HAVE_GIT, IS_LINUX, MACHINE_ARCH # Project directory DATA_DIR = os.path.join( os.path.dirname(os.path.realpath(__file__)), "project", ) # The cache key test uses a project which exercises all plugins, # so we cant run it at all if we dont have them installed. # @pytest.mark.skipif(MACHINE_ARCH != "x86-64", reason="Cache keys depend on architecture") @pytest.mark.skipif(not IS_LINUX, reason="Only available on linux") @pytest.mark.skipif(HAVE_BZR is False, reason="bzr is not available") @pytest.mark.skipif(HAVE_GIT is False, reason="git is not available") @pytest.mark.datafiles(DATA_DIR) def test_cache_key(datafiles, cli): project = str(datafiles) # Workaround bug in recent versions of setuptools: newer # versions of setuptools fail to preserve symbolic links # when creating a source distribution, causing this test # to fail from a dist tarball. goodbye_link = os.path.join(project, "files", "local", "usr", "bin", "goodbye") os.unlink(goodbye_link) os.symlink("hello", goodbye_link) # pytest-datafiles does not copy mode bits # https://github.com/omarkohl/pytest-datafiles/issues/11 os.chmod(goodbye_link, 0o755) check_cache_key_stability(project, cli) buildstream-plugins-2.7.0/tests/cachekey/project/000077500000000000000000000000001515254461100221025ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/cachekey/project/elements/000077500000000000000000000000001515254461100237165ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/cachekey/project/elements/autotools1.bst000066400000000000000000000000731515254461100265420ustar00rootroot00000000000000kind: autotools sources: - kind: local path: files/local buildstream-plugins-2.7.0/tests/cachekey/project/elements/autotools1.expected000066400000000000000000000001001515254461100275420ustar00rootroot0000000000000098ae7c344c01c64d2597338ec632e22a36f1b78ce502a9f3ed668edd0921dcb3buildstream-plugins-2.7.0/tests/cachekey/project/elements/cmake1.bst000066400000000000000000000000671515254461100255740ustar00rootroot00000000000000kind: cmake sources: - kind: local path: files/local buildstream-plugins-2.7.0/tests/cachekey/project/elements/cmake1.expected000066400000000000000000000001001515254461100265710ustar00rootroot00000000000000d76a32a55035f8ca8a31634490400657f73efe6ec5d8fbc28393c98f482262debuildstream-plugins-2.7.0/tests/cachekey/project/elements/make1.bst000066400000000000000000000000661515254461100254300ustar00rootroot00000000000000kind: make sources: - kind: local path: files/local buildstream-plugins-2.7.0/tests/cachekey/project/elements/make1.expected000066400000000000000000000001001515254461100264260ustar00rootroot000000000000009c297771102045d7619e678d952e5b0a8771d9f7afcb11e5bb072f9e84b8724cbuildstream-plugins-2.7.0/tests/cachekey/project/elements/meson1.bst000066400000000000000000000000671515254461100256350ustar00rootroot00000000000000kind: meson sources: - kind: local path: files/local buildstream-plugins-2.7.0/tests/cachekey/project/elements/meson1.expected000066400000000000000000000001001515254461100266320ustar00rootroot00000000000000e971e99926950e605d88f3d65edd1bad584a2b2a23943460aa48250be889622fbuildstream-plugins-2.7.0/tests/cachekey/project/elements/pip1.bst000066400000000000000000000000651515254461100253020ustar00rootroot00000000000000kind: pip sources: - kind: local path: files/local buildstream-plugins-2.7.0/tests/cachekey/project/elements/pip1.expected000066400000000000000000000001001515254461100263010ustar00rootroot00000000000000321bf2ac1384c306b7be050c163ff21a22ba672c6061786bb4015c62abe2ea3fbuildstream-plugins-2.7.0/tests/cachekey/project/elements/setuptools1.bst000066400000000000000000000000741515254461100267330ustar00rootroot00000000000000kind: setuptools sources: - kind: local path: files/local buildstream-plugins-2.7.0/tests/cachekey/project/elements/setuptools1.expected000066400000000000000000000001001515254461100277320ustar00rootroot0000000000000090aab86db2fcc92a7200dce497fe52dce9e96de04ddbb93b04ee065ac9f708efbuildstream-plugins-2.7.0/tests/cachekey/project/files/000077500000000000000000000000001515254461100232045ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/cachekey/project/files/local/000077500000000000000000000000001515254461100242765ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/cachekey/project/files/local/etc/000077500000000000000000000000001515254461100250515ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/cachekey/project/files/local/etc/hello.conf000066400000000000000000000000201515254461100270130ustar00rootroot00000000000000message = Hello buildstream-plugins-2.7.0/tests/cachekey/project/files/local/etc/ponystyle.conf000066400000000000000000000000051515254461100277610ustar00rootroot00000000000000pink buildstream-plugins-2.7.0/tests/cachekey/project/files/local/usr/000077500000000000000000000000001515254461100251075ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/cachekey/project/files/local/usr/bin/000077500000000000000000000000001515254461100256575ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/cachekey/project/files/local/usr/bin/goodbye000077700000000000000000000000001515254461100302532helloustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/cachekey/project/files/local/usr/bin/hello000077500000000000000000000000341515254461100267050ustar00rootroot00000000000000#!/bin/bash echo "Hello !" buildstream-plugins-2.7.0/tests/cachekey/project/files/patches/000077500000000000000000000000001515254461100246335ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/cachekey/project/files/patches/patch.diff000066400000000000000000000001451515254461100265640ustar00rootroot00000000000000--- a/usr/bin/hello +++ b/usr/bin/hello @@ -1,3 +1,3 @@ #!/bin/bash -echo "Hello !" +echo "Bye !" buildstream-plugins-2.7.0/tests/cachekey/project/project.conf000066400000000000000000000004751515254461100244250ustar00rootroot00000000000000# Project config for cache key test name: cachekey min-version: 2.0 aliases: upstream: https://up.stream.org/ plugins: - origin: pip package-name: buildstream-plugins sources: - bzr - cargo - docker - git - patch - pip elements: - autotools - cmake - make - meson - pip - setuptools buildstream-plugins-2.7.0/tests/cachekey/project/sources/000077500000000000000000000000001515254461100235655ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/cachekey/project/sources/bzr1.bst000066400000000000000000000001361515254461100251550ustar00rootroot00000000000000kind: import sources: - kind: bzr url: https://launchpad.net/bzr track: trunk ref: 6622 buildstream-plugins-2.7.0/tests/cachekey/project/sources/bzr1.expected000066400000000000000000000001001515254461100261550ustar00rootroot00000000000000165b0049baa38c815c16186d3ac969edec76b47f4812a84fcf98f1830d862a15buildstream-plugins-2.7.0/tests/cachekey/project/sources/cargo1.bst000066400000000000000000000002361515254461100254540ustar00rootroot00000000000000kind: import sources: - kind: local path: files/local - kind: cargo url: upstream:crates ref: - name: foo version: 1.0 sha: thisisthecratesha buildstream-plugins-2.7.0/tests/cachekey/project/sources/cargo1.expected000066400000000000000000000001001515254461100264530ustar00rootroot00000000000000c58a12e23b6d497196fc724102d06f62c64a73cedc6e12b2b93c6bd1e70dc17fbuildstream-plugins-2.7.0/tests/cachekey/project/sources/cargo2.bst000066400000000000000000000003131515254461100254510ustar00rootroot00000000000000kind: import sources: - kind: local path: files/local - kind: cargo url: upstream:crates vendor-dir: ponies cargo-lock: Pony.lock ref: - name: foo version: 1.0 sha: thisisthecratesha buildstream-plugins-2.7.0/tests/cachekey/project/sources/cargo2.expected000066400000000000000000000001001515254461100264540ustar00rootroot000000000000009013b5ea603e56ce4690fdc7a48ccf9d400e00dcd89cfd115be7d55403f74017buildstream-plugins-2.7.0/tests/cachekey/project/sources/docker1.bst000066400000000000000000000001231515254461100256230ustar00rootroot00000000000000kind: import sources: - kind: docker image: theimage ref: thisistheimagedigest buildstream-plugins-2.7.0/tests/cachekey/project/sources/docker1.expected000066400000000000000000000001001515254461100266270ustar00rootroot00000000000000917564bc9f8a95493ff09c8426fad5eb3bf72f36388830c7a9680e31105126f2buildstream-plugins-2.7.0/tests/cachekey/project/sources/docker2.bst000066400000000000000000000002031515254461100256230ustar00rootroot00000000000000kind: import sources: - kind: docker registry-url: upstream:images image: theimage ref: thisistheimagedigest track: latest buildstream-plugins-2.7.0/tests/cachekey/project/sources/docker2.expected000066400000000000000000000001001515254461100266300ustar00rootroot00000000000000c3a034d31c4a1e4a4a0a34df06bb82ce7e67bb7b014a8eb634c195165fbe98e5buildstream-plugins-2.7.0/tests/cachekey/project/sources/git1.bst000066400000000000000000000001721515254461100251430ustar00rootroot00000000000000kind: import sources: - kind: git url: https://example.com/git/repo.git ref: 6ac68af3e80b7b17c23a3c65233043550a7fa685 buildstream-plugins-2.7.0/tests/cachekey/project/sources/git1.expected000066400000000000000000000001001515254461100261430ustar00rootroot00000000000000e1bdd66a7272b7f329fc17b26195685dbf03fcbd14dbe7e33acc62970e25ee5bbuildstream-plugins-2.7.0/tests/cachekey/project/sources/git2.bst000066400000000000000000000002661515254461100251500ustar00rootroot00000000000000kind: import sources: - kind: git url: https://example.com/git/repo.git ref: 6ac68af3e80b7b17c23a3c65233043550a7fa685 submodules: plugins/foo: url: upstream:foo.git buildstream-plugins-2.7.0/tests/cachekey/project/sources/git2.expected000066400000000000000000000001001515254461100261440ustar00rootroot0000000000000093120bb6865a1ccaefcc031b19f6f5b4fcc84f63929a2a5e1ad4d11bc05ed908buildstream-plugins-2.7.0/tests/cachekey/project/sources/patch1.bst000066400000000000000000000001051515254461100254530ustar00rootroot00000000000000kind: import sources: - kind: patch path: files/patches/patch.diff buildstream-plugins-2.7.0/tests/cachekey/project/sources/patch1.expected000066400000000000000000000001001515254461100264570ustar00rootroot0000000000000085b60ad566f2b7abc8b4aec811d1b675b7aaaa1005376e5b7ac53cbc9a1c1734buildstream-plugins-2.7.0/tests/cachekey/project/sources/patch2.bst000066400000000000000000000001531515254461100254570ustar00rootroot00000000000000kind: import sources: - kind: patch path: files/patches/patch.diff directory: usr/bin strip-level: 1 buildstream-plugins-2.7.0/tests/cachekey/project/sources/patch2.expected000066400000000000000000000001001515254461100264600ustar00rootroot0000000000000065c6520240dd39aed4380f08166a3cf15dcedc7e05966361128b7722b919bfe5buildstream-plugins-2.7.0/tests/cachekey/project/sources/patch3.bst000066400000000000000000000001531515254461100254600ustar00rootroot00000000000000kind: import sources: - kind: patch path: files/patches/patch.diff directory: usr/bin strip-level: 3 buildstream-plugins-2.7.0/tests/cachekey/project/sources/patch3.expected000066400000000000000000000001001515254461100264610ustar00rootroot00000000000000764f81313f2367bdaa748c277055c909a1a9b6c257bf9d721756604e7737744fbuildstream-plugins-2.7.0/tests/cachekey/project/sources/pip1.bst000066400000000000000000000003701515254461100251500ustar00rootroot00000000000000kind: import sources: - kind: git url: https://example.com/foo/foobar.git ref: b99955530263172ed1beae52aed7a33885ef781f - kind: pip url: https://pypi.example.com/simple packages: - horses - ponies ref: 'horses==0.0.1\nponies==0.0.2' buildstream-plugins-2.7.0/tests/cachekey/project/sources/pip1.expected000066400000000000000000000001001515254461100261500ustar00rootroot00000000000000f7357a7441b3ce68be518107f34c94e3f6f8545efd8e951c6d71d56c83bef227buildstream-plugins-2.7.0/tests/cachekey/project/target.bst000066400000000000000000000005461515254461100241070ustar00rootroot00000000000000kind: stack depends: - sources/bzr1.bst - sources/cargo1.bst - sources/cargo2.bst - sources/docker1.bst - sources/docker2.bst - sources/git1.bst - sources/git2.bst - sources/patch1.bst - sources/patch2.bst - sources/patch3.bst - sources/pip1.bst - elements/autotools1.bst - elements/cmake1.bst - elements/make1.bst - elements/meson1.bst - elements/pip1.bst buildstream-plugins-2.7.0/tests/cachekey/project/target.expected000066400000000000000000000001001515254461100251020ustar00rootroot000000000000003dc2ed8ab56f9c706e1fbf4bacc1ab1e38d20f66c686f1ab05d5ad0e504272fcbuildstream-plugins-2.7.0/tests/conftest.py000066400000000000000000000030601515254461100210560ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # import pytest from buildstream._testing import sourcetests_collection_hook from buildstream._testing import register_repo_kind from .testutils.repo import Bzr, Git ################################################# # Implement pytest option # ################################################# def pytest_addoption(parser): parser.addoption( "--integration", action="store_true", default=False, help="Run integration tests", ) def pytest_runtest_setup(item): # Without --integration: skip tests not marked with 'integration' if not item.config.getvalue("integration"): if item.get_closest_marker("integration"): pytest.skip("skipping integration test") register_repo_kind("bzr", Bzr, "buildstream_plugins") register_repo_kind("git", Git, "buildstream_plugins") # This hook enables pytest to collect the templated source tests from # buildstream._testing def pytest_sessionstart(session): sourcetests_collection_hook(session) buildstream-plugins-2.7.0/tests/elements/000077500000000000000000000000001515254461100204745ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/elements/autotools.py000066400000000000000000000073561515254461100231120ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Pylint doesn't play well with fixtures and dependency injection from pytest # pylint: disable=redefined-outer-name import os import pytest from buildstream._testing.integration import integration_cache # pylint: disable=unused-import from buildstream._testing import cli_integration as cli # pylint: disable=unused-import from buildstream._testing.integration import assert_contains from buildstream._testing._utils.site import HAVE_SANDBOX pytestmark = pytest.mark.integration DATA_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "autotools") # Test that an autotools build 'works' - we use the autotools sample # amhello project for this. @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason="Only available with a functioning sandbox") def test_autotools_build(cli, datafiles): project = str(datafiles) checkout = os.path.join(cli.directory, "checkout") element_name = "amhello.bst" result = cli.run(project=project, args=["build", element_name]) assert result.exit_code == 0 result = cli.run(project=project, args=["artifact", "checkout", element_name, "--directory", checkout]) assert result.exit_code == 0 assert_contains( checkout, [ "/usr", "/usr/lib", "/usr/bin", "/usr/share", "/usr/bin/hello", "/usr/share/doc", "/usr/share/doc/amhello", "/usr/share/doc/amhello/README", ], ) # Check the log result = cli.run(project=project, args=["artifact", "log", element_name]) assert result.exit_code == 0 log = result.output # Verify we get expected output exactly once assert log.count("Making all in src") == 1 # Test that an autotools build 'works' - we use the autotools sample # amhello project for this. @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason="Only available with a functioning sandbox") def test_autotools_confroot_build(cli, datafiles): project = str(datafiles) checkout = os.path.join(cli.directory, "checkout") element_name = "amhelloconfroot.bst" result = cli.run(project=project, args=["build", element_name]) assert result.exit_code == 0 result = cli.run(project=project, args=["artifact", "checkout", element_name, "--directory", checkout]) assert result.exit_code == 0 assert_contains( checkout, [ "/usr", "/usr/lib", "/usr/bin", "/usr/share", "/usr/bin/hello", "/usr/share/doc", "/usr/share/doc/amhello", "/usr/share/doc/amhello/README", ], ) # Test running an executable built with autotools @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason="Only available with a functioning sandbox") def test_autotools_run(cli, datafiles): project = str(datafiles) element_name = "amhello.bst" result = cli.run(project=project, args=["build", element_name]) assert result.exit_code == 0 result = cli.run(project=project, args=["shell", element_name, "/usr/bin/hello"]) assert result.exit_code == 0 assert result.output == "Hello World!\nThis is amhello 1.0.\n" buildstream-plugins-2.7.0/tests/elements/autotools/000077500000000000000000000000001515254461100225255ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/elements/autotools/elements/000077500000000000000000000000001515254461100243415ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/elements/autotools/elements/amhello.bst000066400000000000000000000003101515254461100264660ustar00rootroot00000000000000kind: autotools description: Autotools test depends: - base.bst sources: - kind: tar url: project_dir:/files/amhello.tar.gz ref: 9ba123fa4e660929e9a0aa99f0c487b7eee59c5e7594f3284d015640b90f5590 buildstream-plugins-2.7.0/tests/elements/autotools/elements/amhelloconfroot.bst000066400000000000000000000004541515254461100302510ustar00rootroot00000000000000kind: autotools description: Autotools test depends: - base.bst sources: - kind: tar url: project_dir:/files/amhello.tar.gz ref: 9ba123fa4e660929e9a0aa99f0c487b7eee59c5e7594f3284d015640b90f5590 directory: SourceFile variables: conf-root: "%{build-root}/SourceFile" command-subdir: build buildstream-plugins-2.7.0/tests/elements/autotools/elements/base.bst000066400000000000000000000000551515254461100257650ustar00rootroot00000000000000kind: stack depends: - base/alpine-image.bst buildstream-plugins-2.7.0/tests/elements/autotools/elements/base/000077500000000000000000000000001515254461100252535ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/elements/autotools/elements/base/alpine-image.bst000066400000000000000000000003241515254461100303140ustar00rootroot00000000000000kind: import description: Import an alpine image as the platform sources: - kind: tar url: alpine:integration-tests-base.v1.x86_64.tar.xz ref: 3eb559250ba82b64a68d86d0636a6b127aa5f6d25d3601a79f79214dc9703639 buildstream-plugins-2.7.0/tests/elements/autotools/files/000077500000000000000000000000001515254461100236275ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/elements/autotools/files/amhello.tar.gz000066400000000000000000000735331515254461100264120ustar00rootroot00000000000000K2Z\{wƶ|9!sc9$u14rrѱ&wgFNҤU@̞=g؞N|k ף?کMaQcw뛭v}7bK"e$Po/jv^ԍ"׿"c|Ncgh{6+ߜ%6M D+N_C{6X,-/fap*"'tg #7[V?VᾸ'VG-BrJkC?ޭxJ)΃q= f~<1"8񪀀E͓ŋf<$[y-$w:\xjqz:GτN}~.vES5N슳~m q. )嘹dl^S܋7Zt-,"bz&Wg, }7|>. MO602F3@s|H旈[p wL 2.cSRd1MNLt c\ؽLB%T\1h&q@,S{pd$SS“`AoBCŊxiW(`N1)5ڧ~ALQr!p!ʥ-Q-]?<"uy 녱[(86.Ѣ<0sIx&DjƤ65&ADp&ҹduIא){jki22rbkFk+t78`;W5'H<2@l,H\=2/Ծjhw5O>gjLlU8=uNO^[%κϺcq>OzP&(}Wq){t y%Z8I (JdH0)Cj k3p1/ӗ_Scc`̃d l$kK$n;^FT*k"mbnC SvA4݈ba;ؓo4Ԇэ}+Y j92&!R5) fW(_ ҇;Xۇ&`%53{b3i.;/m/'#Trwh, Y¦Fbc?ȁR֢+usn^V%+fTr KN4҉! .]" Sii)CDF RO|M*︄^Fde_ag9u}wL)uW1S5.aaY >ƣ=q{hh}Uh-XN &BkmڤC\#5%3ȩHf}%7wr =@+8jr)-_ƛWa8ȼLj 큎De9m5mK4ݘx@@ʘpKW\]iM3֓5\*nʹ?HڃtI Krya`VJ;eӈNI=V1^KϢݩ†B@*ZL@^2 !=i6voԨzQd:*0?͛P܃{+!w6 ]?+X"(]BwfA>lLH,2A;͊ݜI|w%I>̜/)kuj`.}XeZ|Sĕ@*UkB[FLcXٓO}P=LU䔁tiYANaXkpJ).Q5"ǥ<X|9ie3FfCT"q?xG'ekQf͉#ۈa&9bj\ΖQ\ޑ F ZM GVq7'!E*$}gQ4 *4pXq&\).Z/5wn>e+U뇴'|w B/Ow%;l%qȍ2e@=y}Z G.r SSd:Y,,L+aǼMTCgB A%,Cb1YHk:(e|k-`] jNfslOlMG֍jڧH`]kHڗ_=T*/,Rht_yrz`pPYoX41#^CV<=<׶'*hT(Ӷl8؝ph "0=r#V-jn51|%5%{0x] _2Q|ߡGWU[?1xw{ߩ.䤾gk\V՝䅸m'C[ԝm$s.y;D/)mVfviA&\ٯΤOrϢxt@-*ׁ;R@$->'/8k~l>k{]8QT;%&9|+?t hjIΝ_Wdc-3z\OV*/}/cܦ[7?5jchNruU\ԭ-ZWz<fwz]e W>Jҧ-61V~NOv y~sƻBj "] iwv^zрg:o_㺗+T꼷]:SU;m 3yP 0KTUwT3WOʪÐ*U[q駭 @S^P*b]W_y{k--Dެ-wrYf{:8tɦ '~b;덬\)}Av~٥**/B]&EKz{ zEajqrR)N=kzZZ|iI7Xm,ᕪQ'.)y!*tF#ONpW͐X6hs՗[X^ FTESzҜdJZUp.f:h(yb0%w %l" #?M+\۔.~qh;l[Py2A1&E3 ڬ3%T0v?PVf̽&aQ FM\P&rGt# 䡞{Hx![AxqT#op̩ 8`z_ q>oNMAUrɳ_*TѪ OQ0`ZfciKip6,c[* 62ABa\Zy`ѤRV-jMr#ʳ6a>m2ҕ^p:4t7WX`ܘN*phcr Zn}һjK'9G, maG)S wt eLH%s+bn~mz̳ 6L|S`$~@޲ c;R#:DK`O"&K8f0{&`"U.opPGV]oS dHأIa+V5{:N(ܲC.(?_"`cV->saVСΚ:%Tegɍ ّ1,(̦^d Ba Zj6tyЌt%QBa3LpЌ7Ǧp>ZI7\U2F:o +}J r@ 5y@G]2ÃOw= -l%c>BrH):ңfuf n, Kz6G=LљS\*3Zl>zSH,yءÙͣ;Yw?{YT4U^Kߚv岋CeF8By(rtmC* k$F,%n|~I']Q6Ԟ2MGyt3hzG( x4]ӞmsֿXIFG@͡ CaA#=8P?A^~3Qh4uz| >'.T $^Who|@<>6hw/rW^5Q__9wIFU2Mi7zm |(oaРb4D0>{@CNvjwq\^ζhC? BqIZm6* u-q_]Ixj ?UKRv2E-|Ԝ$`BT(ჼ+`$Y[-!c2y=aJ"#yf>ȯןgI>g~ H!# i18ʝHOSBpJ*V%}VOg(|A0 X:%!r#\cOMإGK<8pVG@9:qj!Ξ I[oϢM/7tiITRN#&Wy[#9ry'܍pF-N B_M ϩ]ۜװ:'a{mm~ToY[-r~$rb R#m [^dv<ݡI}n,7/Gyz  @ Kȓg,unk]R֍qz_ENϣxQZ!J \÷^ALT.y~;$u.NͿZg8R&&~]um,$ޗ6q%k߯ѯ84"j -!f/ع@XRkԒ1TY%aͽHg_ԩ!BE,(ӂ %]PO- 2FaS_v[{)-әrfc͈賓 Dsz Dy6u1e&5!0Ux(L:AhHmY#ZÀ#&ht֛e A/L-aP[8-U03̺;bxbx!,,cSk-=+\v`PޮEvKϲ6TFGm\, vMdfkmi|pd͏ 34+枹on.Ջ1r eΝJ7_#B ])ƕ.e32z 6"84EXCj Ŋ8iGh]N1בiLUbaУ)Q9 l˝Vve- Pk৑WUN`2xA/˨&š+m.*P_ jdmIOP =ėUhqJ]kaLL8IIqʔ?d~HI-CKtH%GQXgSjR󒼻 (o8QuQ#ƧM &X֬N?\)NEI)pGJ2SUC_'s:RhϽ'|vgڏϮ3ޅܨ=(̋a.Fpč۲ Fx \2W d,,- P1 q3;Q>+rfCٮ乫eT }:ڙ5ʝ_ VhY+)3g:w.s͞ MV%׹=ȿ t+ߺ$hD$nE^l&,/?!;qh =SBf `>m&Exz㓶= ܂9xnKP,zT,c5 \{ H!4;8:;k$_do.R6rׄrs ]t-U^wpE$FhK˜"./{D*<>%[ֹ쟇MYcTi47ZF] ;UXū2uVBJoZPI6(}ȍMx`)LaȢA{@^R7WOZ$OS, f5|[6Lq>`-h)/VPrJJ9ĠsjQNS3suG؟)M;j24f` JlhjZYbu"5_^^ۻʞB})/^׆[4ܦ׳L b:)1|We"ok`F7ޮ,Z&1|g*ʖFY|Kui7wy%|E) Qup:Gvj tE _ӭCĬY+:>7/z:(1#˼qLV{PV_'2F%f:-/jSYnFTAi}X}zZ\+Χ%_ lcZ(l]RtK"b㫄X16Jۭau7ן:Hᚔ&;X) qNzw34Xu"Uݳ(.؂r, X4,`Ҝz8K~8* ǒyI~B|%#9޺㾁foO3SmE"oW{:쟷%$=<  )iLG=o?15]M={[+_*Ӟa'4\L8ZXuA;Awg+CקuŲyaBHϑhgo.^HEbQ&EVȨ Sy̕t/BlfL3+-}NDATစipsV/D`+앀ݳn("E4ݺYnֿdqrP?|1l|:qlcaR~υk5j%^~gOWoS!Zߙl{:YYaI^Ɛ,}NIj%ܬ{b*v>ا2Tf>{1̉[5{ȬaːM)S z3=BX"cG3=fOCg>щ@"v^mIea{'[;[jrY%rվwyɛW,P.zyc4!b?6qř8~{S\9͓|4~Sym!7!j{;[:.0F"?8lz}v aFy`Ń {_ӰUe8 aMUL.9iz=:wAp#ffF,f*"~B;~-cYKaF'c-?&TRyY9Lo`%rҽ?6@YDW}{f٤Xځ}C^\") < Hښc̙QZxR;e]F`rzޠ.bWl=a( 1Wbͣh&=v. D~,*ZiuRd|i\+l#ȡA T edέO^ރ=o"گQi/f87tQ;ٸZQɈF&fZJ֋YӅ( +aw"MFuIaDBu栾9mO,"mEx-Od{XUJL֐FƁaurngoK"B(3 Y)Sﶦ9CfX)=4am(=Ajv`L%{ݭtk_/hA[/}^@|d+(6B4uׂ=N\hC{2ze"|Ak\ .kzюx=Ũ@-D;Q%*`JP!jK[8֦陌Deļ(jN2Wh Wv[jZJ̦ 5(' ͳE`غ9?O%;=5C .Hm+|42Z]֤ +1 ˣ^M#jpp1g"+JE뫉(GdeRD RZ1ZQg6߬<LlE4@0pVqnxJɱ:GS\T%6r=$H+y G깊 ;%^ʜ/Tz] ٜyNrL`u#G0aKpK tu;+^X7 6VPRn(P\E}{er鞟'+yy `1BLZ"n UkAtCvǪi{VS#4P5z*℞ *H6a(;{j)Fk&Od0^k SI=#?,JCriT|}]]2|"s"mci nB?vi&eґl-|'7 ]G'z@OIFev3u^P9 `Q7m'_@O)ψA%q1ż9HGG&>П4/'B>Mg39!e6sz_\\{UUÉs$P0Szt6";`ě{~7c##rT[+ cЯ tX~;Xz [4r?u+4GLfMgOEBř)Z-a/#ZX⋒06]ܗ0ߝ;^-}gր#j_TloUy}g^cnSx)@?2pG`tp:[E\hiir2AS0= 6)Lpuh u=m}:X??Uq|ۅ/:)&0h6|N;o~c؍>awo)0. ?ݦ^񘏅˷kQA󚡑s(t<~|&~V(Po噰~GdPr-ʕ+O@^їO/Eg'm5aEBcc| jx_ADUIħ 2(i[h_?#}.McpX%]n++~T>@OzU%a/鵘Óv4PP`~S|\+1Ib_xX,2;H;}~ #m4ou6z qy6S}KGBS CN(;8fq;*] fg)|[$hi"} a. pruǦ?KzC\᭕l;ӠSfvlYe E\^l*䟧'gCb&g٘INO>+l]Ԃqz׊'TF;.^Cw7DŠpz щf`QD镦@ٰզ;[2l,Yp:Ԙm6,zr>5웶 Iaou$lT};^4RFjtOދ}vw) K?5Baz ҉!7΂sA\C~FfPo9¥YWц{>O*@hvp"uIĞA6ۧT:6@#^{yw< acCM]OR(vp2=|]5t2pi ^̛M=5mƠ&ꬦ?M֕* ]FW%kv?8{3v>.qd;0DGl4=[ᇀzOה)@8 't}#xcIupOʯvC\T#z2I3ǐIb͆p$% ;CaQF( aXg_o5>Vk w:%`JЁA2sg}}0e~4T.X>|[}L:[ uBHm9B$AꝯEH%pSqxr垽8M`-;ZCl`;6ǟ\–h>K\#:_"M.b7 bBzs*|~hpOT( ♠Cθņ^wRNQ.F@O0DǚXL.x@=J49a/lʂqPhQ֣rkc k`@TVY䊱hmxqnLy٩⿶b0nW/}x  [ ^T"|A|צ@7@fAmҩ9VIpe 5V=nͨ`uZv)XT +ڴzVqleked ]+-`ӡ bYv.h5cgaCT,LwL k  B- ;qdJOЇ NiVrW`6@AUhK$Ctznoh^WK _eQ^:$N݌XApx*%S_P[TEp>TI*{YZ5KeD#x U ;s3=fx+=a"&XJiTy0Qv^LEn7.~gFkV2nZts3)XZd޴=WVWL-Lb s N=@P7;!'"K3@ 7pwǼFkAvȊT3AZFp|)eh:%MCfܨ`Ռ."e ?5ON~%KJ N8PX,.RڪpbזE>HoZS9sᴮeTODI`Qa(o[EfvPMޏɰ(b?0nswՙLpW\x{u̦ 2Rk mPh'"&=Ŕnm<[<:ũ~ejD_5 }mr-Oe٪S=IV|s[ɉ>E[d2y !QŠD"CIi'kpÚ_ne\u=9oeVbQlRPuo[C25O/wJB9Hl4]B$}d"˪=}-nK9AOǍzgm$ Υ1[Azmz,E~G!эkwRD[4 %0~]#D{fQ\\ Ol@%7ۼ]㞡mCt!X*0XCrv~@,&v3* m٣3]&w.8NfffY#nxɴ, AKaZu|i!Avf{ ,'] +$z t9뒏Zh*Nq}L-f1D܎#N9!/|ͰbvƛWr@Cgf'ȕ~޾ @u,u(Z$aM!_(ؕ vm F-1-{L2 Ⱥ ie=:Ŗ/I%c@/79Ld]4MG~ye_?Yz}CލdSe+|3ihŕq]^3DŽc61Uif5^dKP?pCI=Ei!ͭRgYOlQR8T.i'+7OwئF3l-L4wEwGӉq -<vSkBd{L0ka wrEO92n)S:V(:, Y";WAzh~Wv͜u5gפq\/wI|]ߢY޴&&4vZse1]jsQB 04uTN,Hw+I!=jz:vV~|Ф+?Qc+Oi. L-wk7Wxb}\qgY#@ô zu >ЮQFvǕ`2Gͬ=rqv;6"(Qt# 'PmOc.lyj19Av6^zC9La^2%ʅ+&@59{Om.~U. ݻݻws:t!لxʣ /5^_ {<5$ )dZ0OiÑ]rζ#No8WbhHv)-F~c#akG O+͸^6p*;k"~*WL}GO '`f6åcX0]ϰ=A>ļ.7639û|S.%hR͢ȋ=S~P3p;EBXMbT"Ju?>?(?4ÿiһ-J_fiN#Ǵ k~5SC?VE=eH{yhw4w|t=,baׅ5ek[(;ۇ?Ow1˝}806C't>aV|ӡHE>9Il*q^Sn:> F,ٞ! f]$P݂昽&*;Qk-7v|( vUFPR.iā; N%X.ơ% XX5}b?Mn[Ű)? zR #tJn ȩ{$@rhCXcU~[ |Cpl쁇̫^elMխ@i7PZf68.JTX0m2h4 Tj @VnR0L IC^F*=][!_N磿]n.4ڧ2KDjN;r;#{_D=-])sE_JrB2*<Ždpd춫RQzz3V Oѵڬ-E-M{.ñF!aJT\wHMهch"χ`p?7^% .zdzNgKbʉ˳K vQ{uZ3)^,FH}}WR[,M܊*Al`;Ī r8É )p9~&h`iSyƲ5J}ee56i ~(U߶ϦțfLԏ铼a!bٰIC 'hGo-TKh쵭`.V@b.TQ)b3SdެlX1'P$b@3MyA6)"eG5l-e'J|{_ԖF~[瀅QqÇdž~ *nەF=<.1h*cdF$2Vg$;76!KGAhK .q( *!hljAL5ܟT3!$yd6Dm^^/0{qǢN?\DoP &Z +:\e $ dM6WIEMUW3?(xR88 w|b!G^oAc-׊ԘDM2J6P𴰣Fт0^ʒ1 OY*kT5%6~j,U>4>Dog)he=hyy7sh?k7x0?՗>Dj#q\?N=i@QQ dS8N@d3?ER]qd'q*qMW؊A,;9a錹L{AøSAu(dpxMOZXjAaMfXcyIr!~ -Y 8yuA̓?̎MSwܪ@çu!NT3iNUb߷-uվ4lAJ/z 2 '(xrXo q$eSQj@;$#Yfb6Khc . a/KhҫdtZ7QS"07Q|'K5{V֢|/T<O4_Qgab.cˇه" ʇ&*/xK~R.Q'oe6lTyR_9nv2xtnCf`(ß?I,y%!uA^]ai C5E`C8^35FUB&ie]'T3ѩxI"piTVPn+*Ƅq"_3@C?^JUPyجR{ZY &u_@ -gggPb̯߇ Md;'sZ~ϮIVUX-j T 4A#J]In #O6!vg,-_u3fj5jQ06AO6~~kƗ_ gA){I+>E뙭bS:lZɌ5h]Cսjia{9 tlo1>VJ89ւ4%.{ؒy:3 JI_Th")e3Rv&Լ)W!_ޓh dSJ-PgZU*ѽ{>4M'O8D@~DEjhD(R`ZmĬk\,gxcbĴ(tpfRBj^\ϢTFP^dd"_SF6Z1SщDdHzJìRlrX8yמ;qG0.(񵐗4qe4D5AMW حg dv˖sm`@`!^L$.R*<{} #`20gmPYOM65rb0ǟ Gc-Kۘ ʌTQg} 76(t4<|aKƠzJ? y; ZIvC$g k XY&3 a._S,H+fj~ h%ڠF#1- k)3_zeV~YţϣكC*pۧ)DF@zZ25K9a:Qq<ҲQK(Z~c6p ̩:Y"p*R9K8=lu@ငh7fI1̦=_eIОF{Lm&![oۻD ")1Mbn^qGM*i[eZ{jIe]> W˭nO Vf?yF; Hw澻7NO~6˹+f*A M旕'jBy:hkR bkqvU.c,zbD-[ښ.׿<]3kg#Q^:өe,}7` ^fꈖ~,TQa(mSыv3lLJ!CmԒ5q|@w=V˺t(y)?smkOXG6u6VF6DNh)N,GzLƟ#В]vS#- #L bN2]r5cX4`"2xxC1a`o0ȻfwVtYxÔ8d}ƴUrAf}liʲ~kp-7k@ l ༶ٔ.%_h@@LVvE%Nu"U #(M&0Kl uj^ :adfyG+͋h#r Zg (eBC˒$ 9lcF{unQzݓd_pṸeK뢧6#꽬R@1IYNN:Qe wGjqjR e:I *;\ "fQWK?0Օ!/5uʈՒ+-A[YB).TVާ/Vh(QakOhwҒX,%{d t;Q$op%#S߼{1hjX?kz=ރ- T2ĵ}B ɮ/8!;քNY:g#pژ|gMs4ޡ69R@fRȾXS٦屇 p $Bq3 -E5K$1Gk3̘ ha=m!;YħCc@Mq!+,T*M/έ,u9J)ysb QNFO x>0܊E5n` iBQHd 4&Qk|6{~" f|Ma&xxCҩiikV귫X7k1Hj_!_ R4WKH`@mlō/L={Aj𾎊cD 6l/|8JtTӑDMP*V8Y|Wvl)u|3Hr9I'1zǜI}{{'7:9/5d"E|b9b5J\H7hN ).珖uftB9Q'/'AhvSE~L6B!I@\s [H>ahV4md>F: ɓCWAae.y\J$f;g lu+١:Iz?WALA썫P%R:iuWl {= k̂QGYqJI}H*t>.e8jƥHhph)E7cٽީxCKH\hIF7-Z,*!ArmPd7"9q&$|# \)&g"5]hYiof` W6Jm-ve@tjo h,&>yhV,U7KL(:&,M7IA E=L4Jj36mSx'SRyJOys3Ob- DiWM43T@jL%L6`DCqP^\aX'yAdML0jۓ>\P;7T&¡2$b!H9#Yߴ. BVmV",D.:ug4l CE#;HRgXWt(s7 tr|܀+EDyX"f郩8g 8Pvc( eR9Y9:nh E](ILڳRE/hS[_6e*W.X[`tɠc=ܒ&am$)Fþݏ\-i6Ч@y/yH f}uW`*pp ]u8J_<  <|nB50 Iԩ%ӯ%U]h7J-Z^pkfq>ڑ(ZD@h9K̿kҭM\tR_BoH4 K}D/s ?a UQ!dhJzEHl-?)G!{ZqO 5WCyw0dfT?H~/b[;Ӂ="fy,ʩ.W|,,PŠ/E?M}u7*3\@Qevym|K-z 2 :iRKiH9f˥nM99FŞ.' x:udpH66 #c`,Ȫv;+MKUD45O{bĊ5rL^+@0p#B6BÀN-gm (Qj tY+??_ž0ϋ"υ Hm}:> ,ohi]wx~3A_|UYy0'A{}8Gl{9?>۾0_/.a&ܸ`)eDַy:pH0<,#rv0:{(A f{ raQY0kJȳX7 뿹< E+'c =`qc2RTfb=Zۼ+`D(>u0;"톸HAA!yJJ""Ř[~**}i^@ T 8HFٵf>l08z_( iT6¹dOK?jeY 2nڞ8*Jُl#5IZ=+t,?PGݶxť}LR/L-kښFO wNc!9z_.ga׻x30c*R?7|2H8J!ZU6/~c ৪ίB`X_e-k.EA"ףPd{ ٖ9{rj}l[ͭMS^?嚋`#6'ۻ5>b0b-zrs{rۇTWmoBlo95a }Ol==WK<[Am%H7v[Ԝwt\M7{oI̫g[h{0f}p{og:2>تÓ=H9P nmĘqO%[E[;T lnqh{44pxSr Ɋ.J*o= $΂OO68LaeS1)_d-S4a8MCc9&tR+y-X+Ov?]9L%.F'ϰX1Ӡ0 #:ftrHp ..dk2NeMhOF]@b3<[Y,VG$KdO`+ƭ`1udvsY$?Zx~6bqk@ans:hֺ\ 4jARuƊZpA00׻0Zs orQ'"4N/E&,txzTqwchكŔ] Xw Awq˿ +tVK>Šur NVv!-rb )mn 0zj[cV@vL Xrƾ!/zsf-={AwbI@ _T a_3H`(#ơZ{#%M}a̡,*n(8m.ǪXWVb\c@ >TzuN=!z~m9ٽ Z+3i-trc \@ f.I?xVX CIݡNz|kѢ ]Y'׏=u/-/W/7s9˽} vL-Gho@4fKsYψ1YL\9Z$CybId"A,~5}Dn2"<#ÐȕUI + ,!'ʗ,e:=poq&,{6Le"AW_?W>{$Q{"gqzq({ȿUC*YcKG+'o=ߟ4O^_m,Ay {g+7@\\7.E_ wjU*fhՎbG7%: ' 3˛>پᱟKZ2O_Mgz +`ag,b ;1F;˫ }yN 9 0UQ 6(-\ePϕVX w0^9lЃ0n=+|zx_;(<))t>/,f] : _+Nd3ᢑ!U|f5혾5#Mdl)'S?{WY_ǜv`N߭ m cf,fC'2*+ sezs)VƬljaR>9%+ͬ,Œ|:OQU"rnOgz`^.J$b{Y+>z%rj3#yڼXgr,qaQ8~n RO^rgPh+(Oiݳ"RoЄ?qHr bH$p.}Q^sFsi`|P!6,YI76&x5Wmc iۦ3Ip2N$V7_zTO1j&'CL-bHG ۏ\u'',P` e5`by 7y0+??-ķyCdD)lZ\ԃ>Uуƃj?  ;xփt3F7Ұ(±65(΃*d/h^8it NvlV~qQ~3uhB /7^( kkU8p,:kNͧGqåU 9ܜpٚꍫ0(sMx!?ZabnXWg(x(۞vIH&yTNsTTN娟{79C e%vVysUHT 32Bh#y0_n+F!QT 7]@m]- L ό?S( 4aA>"\, ڰu,`WiD .`|nx 4lY(f:jEZjɦeμl Cl_nAf5nE7jjz&z b+6y*MVV%+ʜ%4GJ!BM45<{=epyFuӹ9Qb8lZ) YT4'Ӆ[~_'E[i;Vy /LfowcK/߇p<|W;>Exey%e 4B]QaQd|[pq_A]. ]KT/? 6m`(i'mnӳ[Ys}>ws}>ws}>ws}>w_Ti2buildstream-plugins-2.7.0/tests/elements/autotools/project.conf000066400000000000000000000004351515254461100250440ustar00rootroot00000000000000# test project config name: test min-version: 2.0 element-path: elements plugins: - origin: pip package-name: buildstream-plugins elements: - autotools aliases: alpine: https://bst-integration-test-images.ams3.cdn.digitaloceanspaces.com/ project_dir: file://{project_dir} buildstream-plugins-2.7.0/tests/elements/cmake.py000066400000000000000000000055551515254461100221400ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Pylint doesn't play well with fixtures and dependency injection from pytest # pylint: disable=redefined-outer-name import os import pytest from buildstream._testing.runcli import cli_integration as cli # pylint: disable=unused-import from buildstream._testing.integration import integration_cache # pylint: disable=unused-import from buildstream._testing.integration import assert_contains from buildstream._testing._utils.site import HAVE_SANDBOX pytestmark = pytest.mark.integration DATA_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "cmake") @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason="Only available with a functioning sandbox") def test_cmake_build(cli, datafiles): project = str(datafiles) checkout = os.path.join(cli.directory, "checkout") element_name = "cmakehello.bst" result = cli.run(project=project, args=["build", element_name]) assert result.exit_code == 0 result = cli.run( project=project, args=["artifact", "checkout", element_name, "--directory", checkout], ) assert result.exit_code == 0 assert_contains(checkout, ["/usr", "/usr/bin", "/usr/bin/hello"]) @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason="Only available with a functioning sandbox") def test_cmake_confroot_build(cli, datafiles): project = str(datafiles) checkout = os.path.join(cli.directory, "checkout") element_name = "cmakeconfroothello.bst" result = cli.run(project=project, args=["build", element_name]) assert result.exit_code == 0 result = cli.run( project=project, args=["artifact", "checkout", element_name, "--directory", checkout], ) assert result.exit_code == 0 assert_contains(checkout, ["/usr", "/usr/bin", "/usr/bin/hello"]) @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason="Only available with a functioning sandbox") def test_cmake_run(cli, datafiles): project = str(datafiles) element_name = "cmakehello.bst" result = cli.run(project=project, args=["build", element_name]) assert result.exit_code == 0 result = cli.run(project=project, args=["shell", element_name, "/usr/bin/hello"]) assert result.exit_code == 0 assert ( result.output == """Hello World! This is hello. """ ) buildstream-plugins-2.7.0/tests/elements/cmake/000077500000000000000000000000001515254461100215545ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/elements/cmake/elements/000077500000000000000000000000001515254461100233705ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/elements/cmake/elements/base.bst000066400000000000000000000001601515254461100250110ustar00rootroot00000000000000kind: stack depends: - base/install-dpkg.bst - base/base-configure.bst - base/alpine-image.bst - base/ninja.bst buildstream-plugins-2.7.0/tests/elements/cmake/elements/base/000077500000000000000000000000001515254461100243025ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/elements/cmake/elements/base/alpine-image.bst000066400000000000000000000003241515254461100273430ustar00rootroot00000000000000kind: import description: Import an alpine image as the platform sources: - kind: tar url: alpine:integration-tests-base.v1.x86_64.tar.xz ref: 3eb559250ba82b64a68d86d0636a6b127aa5f6d25d3601a79f79214dc9703639 buildstream-plugins-2.7.0/tests/elements/cmake/elements/base/base-configure.bst000066400000000000000000000011661515254461100277110ustar00rootroot00000000000000kind: script depends: - filename: base/install-dpkg.bst type: build variables: install-root: / config: commands: - | # Avoid some chowns which fail at dpkg configure time # mv /bin/chown /bin/chown.real ln -s true /bin/chown - | # This is expected to fail, but will configure everything we need # at least for the purpose of building, other dpkg scripts which # require real root privileges will always fail here. DEBIAN_FRONTEND=noninteractive dpkg --configure -a --abort-after=100000 || exit 0 - | # Restore chown # rm -f /bin/chown mv /bin/chown.real /bin/chown buildstream-plugins-2.7.0/tests/elements/cmake/elements/base/install-dpkg.bst000066400000000000000000000006401515254461100274050ustar00rootroot00000000000000kind: manual depends: - filename: base/alpine-image.bst type: build sources: - kind: git url: https://gitlab.com/BuildStream/buildstream-sysroots.git track: dpkg-build ref: ecf14954e4298ce5495f701464339162fad73f30 config: install-commands: - tar xf dpkg-build-sysroot.tar.xz -C %{install-root} --no-same-owner strip-commands: # For some reason, the strip commands were hanging... - echo "none" buildstream-plugins-2.7.0/tests/elements/cmake/elements/base/ninja.bst000066400000000000000000000005321515254461100261130ustar00rootroot00000000000000kind: manual depends: - filename: base/alpine-image.bst config: install-commands: - | install -D -m 0755 ninja %{install-root}%{bindir}/ninja sources: - kind: zip url: https://github.com/ninja-build/ninja/releases/download/v1.9.0/ninja-linux.zip ref: 1b1235f2b0b4df55ac6d80bbe681ea3639c9d2c505c7ff2159a3daf63d196305 base-dir: '' buildstream-plugins-2.7.0/tests/elements/cmake/elements/bst-plugins-experimental-junction.bst000066400000000000000000000003421515254461100326720ustar00rootroot00000000000000kind: junction sources: - kind: tar url: pypi:0c/dd/c2afff7697104f37fd67d98931c402153409bdd2b35442e088460c452f9d/bst-plugins-experimental-1.93.7.tar.gz ref: 0646cf740cdc049c6343059816d36d2181d31aa0d1632107159c737a4332c83c buildstream-plugins-2.7.0/tests/elements/cmake/elements/cmakeconfroothello.bst000066400000000000000000000004511515254461100277600ustar00rootroot00000000000000kind: cmake description: Cmake test depends: - base.bst sources: - kind: tar directory: Source url: project_dir:/files/cmakehello.tar.gz ref: 508266f40dbc5875293bd24c4e50a9eb6b88cbacab742033f7b92f8c087b64e5 variables: conf-root: "%{build-root}/Source" command-subdir: build buildstream-plugins-2.7.0/tests/elements/cmake/elements/cmakehello.bst000066400000000000000000000003131515254461100262030ustar00rootroot00000000000000kind: cmake description: Cmake test depends: - base.bst sources: - kind: tar url: project_dir:/files/cmakehello.tar.gz ref: 508266f40dbc5875293bd24c4e50a9eb6b88cbacab742033f7b92f8c087b64e5 buildstream-plugins-2.7.0/tests/elements/cmake/files/000077500000000000000000000000001515254461100226565ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/elements/cmake/files/cmakehello.tar.gz000066400000000000000000000240001515254461100261050ustar00rootroot00000000000000./cmakehello/0000755000175200017520000000000013227637750011461 5ustar samsam./cmakehello/config.h.in0000644000175200017520000000005713227636401013476 0ustar samsam#define PACKAGE_STRING "${CMAKE_PROJECT_NAME}" ./cmakehello/src/0000755000175200017520000000000013227637750012250 5ustar samsam./cmakehello/src/main.c0000644000175200017520000000051012450526565013332 0ustar samsam/* Copyright (C) 2006-2014 Free Software Foundation, Inc. This program is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. */ #include #include int main (void) { puts ("Hello World!"); puts ("This is " PACKAGE_STRING "."); return 0; } ./cmakehello/src/CMakeLists.txt0000644000175200017520000000020113227637417015001 0ustar samsamadd_executable(hello main.c) message("Bindir is ${BINDIR}") install(TARGETS hello RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) ./cmakehello/CMakeLists.txt0000644000175200017520000000047113227637610014216 0ustar samsamcmake_minimum_required(VERSION 2.6) # Note that we need to be explicit that we only require C, if not then CMake # will require a C++ compiler to be present. project(hello C) include("GNUInstallDirs") configure_file(config.h.in config.h) include_directories(${CMAKE_CURRENT_BINARY_DIR}) add_subdirectory(src) buildstream-plugins-2.7.0/tests/elements/cmake/project.conf000066400000000000000000000006651515254461100241000ustar00rootroot00000000000000# test project config name: test min-version: 2.0 element-path: elements plugins: - origin: pip package-name: buildstream-plugins sources: - git elements: - cmake - origin: junction junction: bst-plugins-experimental-junction.bst sources: - zip aliases: alpine: https://bst-integration-test-images.ams3.cdn.digitaloceanspaces.com/ pypi: https://files.pythonhosted.org/packages/ project_dir: file://{project_dir} buildstream-plugins-2.7.0/tests/elements/make.py000066400000000000000000000045041515254461100217660ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Pylint doesn't play well with fixtures and dependency injection from pytest # pylint: disable=redefined-outer-name import os import pytest from buildstream._testing.integration import assert_contains from buildstream._testing.integration import integration_cache # pylint: disable=unused-import from buildstream._testing.runcli import cli_integration as cli # pylint: disable=unused-import from buildstream._testing._utils.site import HAVE_SANDBOX pytestmark = pytest.mark.integration DATA_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "make") # Test that a make build 'works' - we use the make sample # makehello project for this. @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason="Only available with a functioning sandbox") def test_make_build(cli, datafiles): project = str(datafiles) checkout = os.path.join(cli.directory, "checkout") element_name = "makehello.bst" result = cli.run(project=project, args=["build", element_name]) assert result.exit_code == 0 result = cli.run( project=project, args=["artifact", "checkout", element_name, "--directory", checkout], ) assert result.exit_code == 0 assert_contains(checkout, ["/usr", "/usr/bin", "/usr/bin/hello"]) # Test running an executable built with make @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason="Only available with a functioning sandbox") def test_make_run(cli, datafiles): project = str(datafiles) element_name = "makehello.bst" result = cli.run(project=project, args=["build", element_name]) assert result.exit_code == 0 result = cli.run(project=project, args=["shell", element_name, "/usr/bin/hello"]) assert result.exit_code == 0 assert result.output == "Hello, world\n" buildstream-plugins-2.7.0/tests/elements/make/000077500000000000000000000000001515254461100214115ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/elements/make/elements/000077500000000000000000000000001515254461100232255ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/elements/make/elements/base.bst000066400000000000000000000000551515254461100246510ustar00rootroot00000000000000kind: stack depends: - base/alpine-image.bst buildstream-plugins-2.7.0/tests/elements/make/elements/base/000077500000000000000000000000001515254461100241375ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/elements/make/elements/base/alpine-image.bst000066400000000000000000000003241515254461100272000ustar00rootroot00000000000000kind: import description: Import an alpine image as the platform sources: - kind: tar url: alpine:integration-tests-base.v1.x86_64.tar.xz ref: 3eb559250ba82b64a68d86d0636a6b127aa5f6d25d3601a79f79214dc9703639 buildstream-plugins-2.7.0/tests/elements/make/elements/makehello.bst000066400000000000000000000003001515254461100256710ustar00rootroot00000000000000kind: make description: make test depends: - base.bst sources: - kind: tar url: project_dir:/files/makehello.tar.gz ref: fd342a36503a0a0dd37b81ddb4d2b78bd398d912d813339e0de44a6b6c393b8e buildstream-plugins-2.7.0/tests/elements/make/files/000077500000000000000000000000001515254461100225135ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/elements/make/files/makehello.tar.gz000066400000000000000000000006601515254461100256050ustar00rootroot00000000000000G[KO@`쯘 6I!&zb-YZ=ۇ]t&FFQҁ@]ԴXfYt)PfmUeKh"Kېx}Ĵ?Gͬõ7mkUfۮZ@κ^ŝ|AH~3炐2xQ~A/1!PYH-Onng׺/1IiUG܆GrOџVQwX8waUX]<`$OOe!եX!<oeRgfEicJPĶ!MBC`mOB!B!B_%gd(buildstream-plugins-2.7.0/tests/elements/make/project.conf000066400000000000000000000004301515254461100237230ustar00rootroot00000000000000# test project config name: test min-version: 2.0 element-path: elements plugins: - origin: pip package-name: buildstream-plugins elements: - make aliases: alpine: https://bst-integration-test-images.ams3.cdn.digitaloceanspaces.com/ project_dir: file://{project_dir} buildstream-plugins-2.7.0/tests/elements/meson.py000066400000000000000000000055101515254461100221700ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Pylint doesn't play well with fixtures and dependency injection from pytest # pylint: disable=redefined-outer-name import os import pytest from buildstream._testing.runcli import cli_integration as cli # pylint: disable=unused-import from buildstream._testing.integration import integration_cache # pylint: disable=unused-import from buildstream._testing.integration import assert_contains from buildstream._testing._utils.site import HAVE_SANDBOX pytestmark = pytest.mark.integration DATA_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "meson") @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason="Only available with a functioning sandbox") def test_meson_build(cli, datafiles): project = str(datafiles) checkout = os.path.join(cli.directory, "checkout") element_name = "mesonhello.bst" result = cli.run(project=project, args=["build", element_name]) assert result.exit_code == 0 result = cli.run( project=project, args=["artifact", "checkout", element_name, "--directory", checkout], ) assert result.exit_code == 0 assert_contains(checkout, ["/usr", "/usr/bin", "/usr/bin/hello"]) @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason="Only available with a functioning sandbox") def test_meson_confroot_build(cli, datafiles): project = str(datafiles) checkout = os.path.join(cli.directory, "checkout") element_name = "mesonconfroothello.bst" result = cli.run(project=project, args=["build", element_name]) assert result.exit_code == 0 result = cli.run( project=project, args=["artifact", "checkout", element_name, "--directory", checkout], ) assert result.exit_code == 0 assert_contains(checkout, ["/usr", "/usr/bin", "/usr/bin/hello"]) @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason="Only available with a functioning sandbox") def test_meson_run(cli, datafiles): project = str(datafiles) element_name = "mesonhello.bst" result = cli.run(project=project, args=["build", element_name]) assert result.exit_code == 0 result = cli.run(project=project, args=["shell", element_name, "/usr/bin/hello"]) assert result.exit_code == 0 assert result.output == """Hello, World!\n""" buildstream-plugins-2.7.0/tests/elements/meson/000077500000000000000000000000001515254461100216155ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/elements/meson/elements/000077500000000000000000000000001515254461100234315ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/elements/meson/elements/base.bst000066400000000000000000000002011515254461100250460ustar00rootroot00000000000000kind: stack depends: - base/install-dpkg.bst - base/base-configure.bst - base/alpine-image.bst - base/ninja.bst - base/meson.bst buildstream-plugins-2.7.0/tests/elements/meson/elements/base/000077500000000000000000000000001515254461100243435ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/elements/meson/elements/base/alpine-image.bst000066400000000000000000000003241515254461100274040ustar00rootroot00000000000000kind: import description: Import an alpine image as the platform sources: - kind: tar url: alpine:integration-tests-base.v1.x86_64.tar.xz ref: 3eb559250ba82b64a68d86d0636a6b127aa5f6d25d3601a79f79214dc9703639 buildstream-plugins-2.7.0/tests/elements/meson/elements/base/base-configure.bst000066400000000000000000000011661515254461100277520ustar00rootroot00000000000000kind: script depends: - filename: base/install-dpkg.bst type: build variables: install-root: / config: commands: - | # Avoid some chowns which fail at dpkg configure time # mv /bin/chown /bin/chown.real ln -s true /bin/chown - | # This is expected to fail, but will configure everything we need # at least for the purpose of building, other dpkg scripts which # require real root privileges will always fail here. DEBIAN_FRONTEND=noninteractive dpkg --configure -a --abort-after=100000 || exit 0 - | # Restore chown # rm -f /bin/chown mv /bin/chown.real /bin/chown buildstream-plugins-2.7.0/tests/elements/meson/elements/base/install-dpkg.bst000066400000000000000000000006401515254461100274460ustar00rootroot00000000000000kind: manual depends: - filename: base/alpine-image.bst type: build sources: - kind: git url: https://gitlab.com/BuildStream/buildstream-sysroots.git track: dpkg-build ref: ecf14954e4298ce5495f701464339162fad73f30 config: install-commands: - tar xf dpkg-build-sysroot.tar.xz -C %{install-root} --no-same-owner strip-commands: # For some reason, the strip commands were hanging... - echo "none" buildstream-plugins-2.7.0/tests/elements/meson/elements/base/meson.bst000066400000000000000000000002741515254461100262010ustar00rootroot00000000000000kind: setuptools depends: - filename: base/alpine-image.bst sources: - kind: git url: https://github.com/mesonbuild/meson.git ref: 0.51.2-0-g6857936c592d6f9608add5a74a51ee405aaddc0d buildstream-plugins-2.7.0/tests/elements/meson/elements/base/ninja.bst000066400000000000000000000005321515254461100261540ustar00rootroot00000000000000kind: manual depends: - filename: base/alpine-image.bst config: install-commands: - | install -D -m 0755 ninja %{install-root}%{bindir}/ninja sources: - kind: zip url: https://github.com/ninja-build/ninja/releases/download/v1.9.0/ninja-linux.zip ref: 1b1235f2b0b4df55ac6d80bbe681ea3639c9d2c505c7ff2159a3daf63d196305 base-dir: '' buildstream-plugins-2.7.0/tests/elements/meson/elements/bst-plugins-experimental-junction.bst000066400000000000000000000003421515254461100327330ustar00rootroot00000000000000kind: junction sources: - kind: tar url: pypi:0c/dd/c2afff7697104f37fd67d98931c402153409bdd2b35442e088460c452f9d/bst-plugins-experimental-1.93.7.tar.gz ref: 0646cf740cdc049c6343059816d36d2181d31aa0d1632107159c737a4332c83c buildstream-plugins-2.7.0/tests/elements/meson/elements/mesonconfroothello.bst000066400000000000000000000004371515254461100300660ustar00rootroot00000000000000kind: meson description: meson test depends: - base.bst sources: - kind: tar directory: Source url: project_dir:/files/mesonhello.tar.gz ref: dbfa22f02c82c83493596cde465a7ed4c39d8d412da3d8ac3b24c3045781f3b2 variables: conf-root: "%{build-root}/Source" command-subdir: build buildstream-plugins-2.7.0/tests/elements/meson/elements/mesonhello.bst000066400000000000000000000002651515254461100263130ustar00rootroot00000000000000kind: meson depends: - filename: base.bst sources: - kind: tar url: project_dir:/files/mesonhello.tar.gz ref: dbfa22f02c82c83493596cde465a7ed4c39d8d412da3d8ac3b24c3045781f3b2 buildstream-plugins-2.7.0/tests/elements/meson/files/000077500000000000000000000000001515254461100227175ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/elements/meson/files/mesonhello.tar.gz000066400000000000000000000022531515254461100262150ustar00rootroot00000000000000Xmo6g!69ߒŦcdHJ Zchr`Qv5Z v {'x8!}2zԀiC+/8{B<[ LA(X&cic -Yv %X" C-iIYBD nE2rt^0@Q݉XV/(@iJbk'B- 6=(TZXdq A .b}]_)-ŜPdRN,1eo<ʵD_H,[Z$XKBBbsl79uAOqVw]jɒ|0iz,4, a6D!)8L}3k̜p]#| ^@432폯{ z\h4@L94&i_8c'6 ]ms`C5}^S/H?@}dMdEз؀`dǚW软7Q#o<(}1[* ?{b_BC+>ölDH뇎040J?|P95@'d{tWA=nT'3Ct*a@1 ~pz|t_^u<."@„0gJDPh@mTd5XgRL/rߧ`BxoտkmL-X+>pkr|#}ϞViZV`WDžplqt #69'~Сh4c}%Kȳ oT[÷r/iW]eTby~Ry,ds3!"aD o{~([Էwh![U5(&li-U_Vm&UzGc埼īX, #z)5S7;PM'_LN/e_t6j`6gS!48NWU#wy4HY2O\]OhUkw># ZhFS %Cݱ4gIV vxhQa̹- РUm$B<w=Zܨ[(Gv쒟ħ'&$J,>k|QݮY"r/-=%{_(7qiPFWgYZbuildstream-plugins-2.7.0/tests/elements/pip/project.conf000066400000000000000000000004521515254461100236020ustar00rootroot00000000000000# test project config name: test min-version: 2.0 element-path: elements plugins: - origin: pip package-name: buildstream-plugins sources: - pip elements: - pip aliases: alpine: https://bst-integration-test-images.ams3.cdn.digitaloceanspaces.com/ project_dir: file://{project_dir} buildstream-plugins-2.7.0/tests/elements/setuptools.py000066400000000000000000000043421515254461100232720ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Pylint doesn't play well with fixtures and dependency injection from pytest # pylint: disable=redefined-outer-name import os import pytest from buildstream._testing.runcli import cli_integration as cli # pylint: disable=unused-import from buildstream._testing.integration import integration_cache # pylint: disable=unused-import from buildstream._testing.integration import assert_contains from buildstream._testing._utils.site import HAVE_SANDBOX pytestmark = pytest.mark.integration DATA_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "setuptools") @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason="Only available with a functioning sandbox") def test_setuptools_build(cli, datafiles): project = str(datafiles) checkout = os.path.join(cli.directory, "checkout") element_name = "setuptoolshello.bst" result = cli.run(project=project, args=["build", element_name]) assert result.exit_code == 0 result = cli.run( project=project, args=["artifact", "checkout", element_name, "--directory", checkout], ) assert result.exit_code == 0 assert_contains(checkout, ["/usr", "/usr/bin", "/usr/bin/hello"]) @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason="Only available with a functioning sandbox") def test_setuptools_run(cli, datafiles): project = str(datafiles) element_name = "setuptoolshello.bst" result = cli.run(project=project, args=["build", element_name]) assert result.exit_code == 0 result = cli.run(project=project, args=["shell", element_name, "/usr/bin/hello"]) assert result.exit_code == 0 assert result.output == """Hello World!\n""" buildstream-plugins-2.7.0/tests/elements/setuptools/000077500000000000000000000000001515254461100227155ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/elements/setuptools/elements/000077500000000000000000000000001515254461100245315ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/elements/setuptools/elements/base.bst000066400000000000000000000000551515254461100261550ustar00rootroot00000000000000kind: stack depends: - base/alpine-image.bst buildstream-plugins-2.7.0/tests/elements/setuptools/elements/base/000077500000000000000000000000001515254461100254435ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/elements/setuptools/elements/base/alpine-image.bst000066400000000000000000000003241515254461100305040ustar00rootroot00000000000000kind: import description: Import an alpine image as the platform sources: - kind: tar url: alpine:integration-tests-base.v1.x86_64.tar.xz ref: 3eb559250ba82b64a68d86d0636a6b127aa5f6d25d3601a79f79214dc9703639 buildstream-plugins-2.7.0/tests/elements/setuptools/elements/setuptoolshello.bst000066400000000000000000000002771515254461100305160ustar00rootroot00000000000000kind: setuptools depends: - filename: base.bst sources: - kind: tar url: project_dir:/files/setuptoolshello.tar.gz ref: c281d5650a104b624c77676c30a456ba1c670654bcf6f24edccb9b9848513cae buildstream-plugins-2.7.0/tests/elements/setuptools/files/000077500000000000000000000000001515254461100240175ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/elements/setuptools/files/setuptoolshello.tar.gz000066400000000000000000000024761515254461100304240ustar00rootroot00000000000000Y[oF34}p"Q/`'@l`4*6] fԪ{o.ޤz]>Y!sg\ %M1x$!:ND<~bgZtHof`|y Ɍ1KF7!`MD/44O'3f$4vdSrl5vՔs!E%cokCC;~؍w꛶gfojZNUa4TQ,]gb)(88VCaeBUy8,iFgkXd$4TaQ ld `@54(fDI,ஓKT\ܒ"ssDAȂ\MDH{(Ē¡WJFBJb%J@um$,Q.yC( <>u**-H"\A9*Xa,RY>Y4$TeoXJA ]ѻGʄ2E\Rn1#2ϳMB&d4" KV$dDLQ|"3l7a]ݸ  8e_8-v1^84œĐ8:9`y0qaPk:W> k 8`7eT0*x2LYvt5K8G9IlTF`2=ll!6skd7ray`p}52\\3ږ}sl~" ̷ohFҔb\Ƶ.> D⹉#sc kf!WlzhJg[-;bS(]+zmy ky2!3VNp %(g-2`DE6m*^۸]MD 2=$Kp-^;R7]TYs2-OC"Hr*YBo 4c(buildstream-plugins-2.7.0/tests/elements/setuptools/project.conf000066400000000000000000000004361515254461100252350ustar00rootroot00000000000000# test project config name: test min-version: 2.0 element-path: elements plugins: - origin: pip package-name: buildstream-plugins elements: - setuptools aliases: alpine: https://bst-integration-test-images.ams3.cdn.digitaloceanspaces.com/ project_dir: file://{project_dir} buildstream-plugins-2.7.0/tests/sources/000077500000000000000000000000001515254461100203435ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/__init__.py000066400000000000000000000000001515254461100224420ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/bzr.py000066400000000000000000000104331515254461100215130ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Pylint doesn't play well with fixtures and dependency injection from pytest # pylint: disable=redefined-outer-name import os import subprocess import pytest from buildstream import _yaml from buildstream._testing import cli # pylint: disable=unused-import from buildstream._testing import create_repo from buildstream._testing import generate_element from tests.testutils.site import HAVE_BZR pytestmark = pytest.mark.skipif(HAVE_BZR is False, reason="bzr is not available") DATA_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "bzr") @pytest.mark.datafiles(os.path.join(DATA_DIR)) def test_fetch_checkout(cli, tmpdir, datafiles): project = str(datafiles) checkoutdir = os.path.join(str(tmpdir), "checkout") repo = create_repo("bzr", str(tmpdir)) ref = repo.create(os.path.join(project, "basic")) # Write out our test target element = {"kind": "import", "sources": [repo.source_config(ref=ref)]} generate_element(project, "target.bst", element) # Fetch, build, checkout result = cli.run(project=project, args=["source", "fetch", "target.bst"]) assert result.exit_code == 0 result = cli.run(project=project, args=["build", "target.bst"]) assert result.exit_code == 0 result = cli.run(project=project, args=["artifact", "checkout", "target.bst", "--directory", checkoutdir]) assert result.exit_code == 0 # Assert we checked out the file as it was commited with open(os.path.join(checkoutdir, "test"), encoding="utf-8") as f: text = f.read() assert text == "test\n" @pytest.mark.datafiles(DATA_DIR) def test_open_bzr_customize(cli, tmpdir, datafiles): project = str(datafiles) repo = create_repo("bzr", str(tmpdir)) ref = repo.create(os.path.join(project, "basic")) element = {"kind": "import", "sources": [repo.source_config(ref=ref)]} generate_element(project, "target.bst", element) workspace = os.path.join(datafiles, "bzr-workspace") result = cli.run(cwd=project, project=project, args=["workspace", "open", "--directory", workspace, "target.bst"]) result.assert_success() # Check that the .bzr dir exists assert os.path.isdir(os.path.join(workspace, ".bzr")) # Check that the correct origin branch is set element_config = _yaml.load(os.path.join(project, "target.bst"), shortname=None) source_config = element_config.get_sequence("sources").mapping_at(0) output = subprocess.check_output(["bzr", "info"], cwd=workspace) stripped_url = source_config.get_str("url").lstrip("file:///") expected_output_str = "checkout of branch: /{}/{}".format(stripped_url, source_config.get_str("track")) assert expected_output_str in str(output) @pytest.mark.datafiles(os.path.join(DATA_DIR)) def test_show_source_info(cli, tmpdir, datafiles): project = str(datafiles) repo = create_repo("bzr", str(tmpdir)) ref = repo.create(os.path.join(project, "basic")) # Write out our test target source_config = repo.source_config(ref=ref) source_config["version"] = "1.2.3" element = {"kind": "import", "sources": [source_config]} generate_element(project, "target.bst", element) # Get the source info result = cli.run(project=project, args=["show", "--format", "%{name}:\n%{source-info}", "target.bst"]) result.assert_success() # Check the results loaded = _yaml.load_data(result.output) sources = loaded.get_sequence("target.bst") source_info = sources.mapping_at(0) assert source_info.get_str("kind") == "bzr" assert source_info.get_str("url") == "file://" + repo.repo assert source_info.get_str("medium") == "bzr" assert source_info.get_str("version-type") == "commit" assert source_info.get_str("version") == "1" assert source_info.get_str("version-guess") == "1.2.3" buildstream-plugins-2.7.0/tests/sources/bzr/000077500000000000000000000000001515254461100211405ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/bzr/basic/000077500000000000000000000000001515254461100222215ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/bzr/basic/test000066400000000000000000000000051515254461100231160ustar00rootroot00000000000000test buildstream-plugins-2.7.0/tests/sources/bzr/project.conf000066400000000000000000000001721515254461100234550ustar00rootroot00000000000000# Basic Project name: foo min-version: 2.0 plugins: - origin: pip package-name: buildstream-plugins sources: - bzr buildstream-plugins-2.7.0/tests/sources/cargo.py000066400000000000000000000052751515254461100220210ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Pylint doesn't play well with fixtures and dependency injection from pytest # pylint: disable=redefined-outer-name import os import pytest from buildstream import _yaml from buildstream._testing import cli # pylint: disable=unused-import DATA_DIR = os.path.join( os.path.dirname(os.path.realpath(__file__)), "cargo", ) def generate_project(project_dir): project_file = os.path.join(project_dir, "project.conf") _yaml.roundtrip_dump( { "name": "foo", "min-version": "2.0", "element-path": "elements", "plugins": [ { "origin": "pip", "package-name": "buildstream-plugins", "sources": ["cargo"], } ], }, project_file, ) @pytest.mark.datafiles(os.path.join(DATA_DIR, "minimal")) def test_cargo_track_fetch_build(cli, datafiles): project = str(datafiles) generate_project(project) # First track result = cli.run(project=project, args=["source", "track", "base64.bst"]) result.assert_success() # Now we should be able to get the source info result = cli.run(project=project, args=["show", "--format", "%{name}:\n%{source-info}", "base64.bst"]) result.assert_success() loaded = _yaml.load_data(result.output) sources = loaded.get_sequence("base64.bst") # Assert the cargo source, which is in the second position after the local source source_info = sources.mapping_at(1) assert source_info.get_str("kind") == "cargo" assert source_info.get_str("url") == "https://static.crates.io/crates/base64/base64-0.22.1.crate" assert source_info.get_str("medium") == "remote-file" assert source_info.get_str("version-type") == "sha256" assert source_info.get_str("version") == "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" assert source_info.get_str("version-guess") == "0.22.1" # Now fetch and build result = cli.run(project=project, args=["source", "fetch", "base64.bst"]) result.assert_success() result = cli.run(project=project, args=["build", "base64.bst"]) result.assert_success() buildstream-plugins-2.7.0/tests/sources/cargo/000077500000000000000000000000001515254461100214365ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/cargo/minimal/000077500000000000000000000000001515254461100230645ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/cargo/minimal/elements/000077500000000000000000000000001515254461100247005ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/cargo/minimal/elements/base64.bst000066400000000000000000000001141515254461100264720ustar00rootroot00000000000000kind: import sources: - kind: local path: files/Cargo.lock - kind: cargo buildstream-plugins-2.7.0/tests/sources/cargo/minimal/files/000077500000000000000000000000001515254461100241665ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/cargo/minimal/files/Cargo.lock000066400000000000000000000003131515254461100260700ustar00rootroot00000000000000version = 3 [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" buildstream-plugins-2.7.0/tests/sources/docker.py000066400000000000000000000130141515254461100221630ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Pylint doesn't play well with fixtures and dependency injection from pytest # pylint: disable=redefined-outer-name # Pylint and responses don't play well together # pylint: disable=no-member import os import pytest import responses from ruamel.yaml import YAML from buildstream import _yaml from buildstream.exceptions import ErrorDomain from buildstream._testing import cli # pylint: disable=unused-import DATA_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "docker") def create_element(yaml, element_name, element_payload, project): with open(os.path.join(project, "elements", element_name), "w", encoding="utf-8") as element_handle: yaml.dump(element_payload, element_handle) @pytest.mark.datafiles(DATA_DIR) def test_docker_fetch(cli, datafiles): project = str(datafiles) result = cli.run(project=project, args=["source", "fetch", "dockerhub-alpine.bst"]) result.assert_success() @pytest.mark.datafiles(DATA_DIR) def test_docker_source_checkout(cli, datafiles): project = str(datafiles) checkout = os.path.join(cli.directory, "checkout") result = cli.run( project=project, args=[ "source", "checkout", "--directory", checkout, "dockerhub-alpine.bst", ], ) result.assert_success() # Rather than make assertions about the whole Alpine Linux image, verify # that the /etc/os-release file exists as a sanity check. assert os.path.isfile(os.path.join(checkout, "dockerhub-alpine/etc/os-release")) @pytest.mark.datafiles(DATA_DIR) @responses.activate def test_handle_network_error(cli, datafiles): # allow manifest to be fetched responses.add_passthru( "https://registry.hub.docker.com/v2/library/alpine/manifests/" "sha256%3A4b8ffaaa896d40622ac10dc6662204f429f1c8c5714be62a6493a7895f66409" ) # allow authentication to go through responses.add_passthru( "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/alpine:pull" ) # By not adding a rule for the blob, accessing # "https://registry.hub.docker.com/v2/" \ # "library/alpine/blobs/sha256%3Ab56ae66c29370df48e7377c8f9baa744a3958058a766793f821dadcb144a4647" # will throw a `ConnectionError`. # attempt to fetch source project = str(datafiles) result = cli.run(project=project, args=["source", "fetch", "dockerhub-alpine.bst"]) # check that error is thrown result.assert_task_error(ErrorDomain.SOURCE, None) # check that BuildStream still runs normally result = cli.run(project=project, args=["show", "dockerhub-alpine.bst"]) result.assert_success() @pytest.mark.datafiles(DATA_DIR) def test_show_source_info(cli, datafiles): # Get the source info project = str(datafiles) result = cli.run(project=project, args=["show", "--format", "%{name}:\n%{source-info}", "dockerhub-alpine.bst"]) result.assert_success() # Check the results loaded = _yaml.load_data(result.output) sources = loaded.get_sequence("dockerhub-alpine.bst") source_info = sources.mapping_at(0) assert source_info.get_str("kind") == "docker" assert source_info.get_str("url") == "https://registry.hub.docker.com" assert source_info.get_str("medium") == "oci-image" assert source_info.get_str("version-type") == "oci-digest" assert source_info.get_str("version") == "sha256:4b8ffaaa896d40622ac10dc6662204f429f1c8c5714be62a6493a7895f664098" assert source_info.get_str("version-guess") == "1.2.3" extra_data = source_info.get_mapping("extra-data", None) assert extra_data is not None assert extra_data.get_str("image-name", "library/alpine") @pytest.mark.datafiles(DATA_DIR) def test_fetch_duplicate_layers(cli, datafiles): # test that fetching a layer twice does not break the mirror directory project = str(datafiles) yaml = YAML() yaml.default_flow_style = False # images to pull alpine_element = "alpine.bst" alpine310 = { "kind": "import", "sources": [{"kind": "docker", "image": "library/alpine", "track": "3.10"}], } create_element(yaml, alpine_element, alpine310, project) cli.run(project=project, args=["source", "track", alpine_element]).assert_success() cli.run(project=project, args=["source", "fetch", alpine_element]).assert_success() # this image uses alpine3:10 as base a base layer # shared layer has digest 03901b4a2ea88eeaad62dbe59b072b28b6efa00491962b8741081c5df50c65e0 python36_element = "python36.bst" python36_alpine310 = { "kind": "import", "sources": [ { "kind": "docker", "image": "library/python", "track": "3.6-alpine3.10", } ], } create_element(yaml, python36_element, python36_alpine310, project) cli.run(project=project, args=["source", "track", python36_element]).assert_success() cli.run(project=project, args=["source", "fetch", python36_element]).assert_success() buildstream-plugins-2.7.0/tests/sources/docker/000077500000000000000000000000001515254461100216125ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/docker/elements/000077500000000000000000000000001515254461100234265ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/docker/elements/dockerhub-alpine.bst000066400000000000000000000003411515254461100273520ustar00rootroot00000000000000kind: import description: Import an Alpine Linux image from Docker hub sources: - kind: docker image: library/alpine track: latest ref: 4b8ffaaa896d40622ac10dc6662204f429f1c8c5714be62a6493a7895f664098 version: 1.2.3 buildstream-plugins-2.7.0/tests/sources/docker/project.conf000066400000000000000000000005161515254461100241310ustar00rootroot00000000000000name: test min-version: 2.0 element-path: elements plugins: - origin: pip package-name: buildstream-plugins sources: - docker aliases: alpine: https://bst-integration-test-images.ams3.cdn.digitaloceanspaces.com/ options: arch: type: arch description: Current architecture values: - x86-64 - aarch64 buildstream-plugins-2.7.0/tests/sources/git.py000066400000000000000000001352121515254461100215040ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Authors: Tristan Van Berkom # Jonathan Maw # William Salmon # # Pylint doesn't play well with fixtures and dependency injection from pytest # pylint: disable=redefined-outer-name import os import subprocess import shutil import pytest from buildstream import Node from buildstream.exceptions import ErrorDomain from buildstream.plugin import CoreWarnings from buildstream._testing import cli # pylint: disable=unused-import from buildstream._testing import generate_project, generate_element, load_yaml from buildstream._testing import create_repo from buildstream import _yaml from tests.testutils.site import HAVE_GIT, HAVE_OLD_GIT DATA_DIR = os.path.join( os.path.dirname(os.path.realpath(__file__)), "git", ) def generate_project_with_git(project, config): config["plugins"] = [{"origin": "pip", "package-name": "buildstream-plugins", "sources": ["git"]}] generate_project(project, config) @pytest.mark.skipif(HAVE_GIT is False, reason="git is not available") @pytest.mark.datafiles(os.path.join(DATA_DIR, "template")) def test_fetch_bad_ref(cli, tmpdir, datafiles): project = str(datafiles) # Create the repo from 'repofiles' subdir repo = create_repo("git", str(tmpdir)) repo.create(os.path.join(project, "repofiles")) # Write out our test target with a bad ref element = {"kind": "import", "sources": [repo.source_config(ref="5")]} generate_element(project, "target.bst", element) # Assert that fetch raises an error here result = cli.run(project=project, args=["source", "fetch", "target.bst"]) result.assert_main_error(ErrorDomain.STREAM, None) result.assert_task_error(ErrorDomain.SOURCE, None) @pytest.mark.skipif(HAVE_GIT is False, reason="git is not available") @pytest.mark.skipif(HAVE_OLD_GIT, reason="old git cannot clone a shallow repo to stage the source") @pytest.mark.datafiles(os.path.join(DATA_DIR, "template")) def test_fetch_shallow(cli, tmpdir, datafiles): project = str(datafiles) workspacedir = os.path.join(str(tmpdir), "workspace") # Create the repo from 'repofiles' subdir repo = create_repo("git", str(tmpdir)) repo.create(os.path.join(project, "repofiles")) first_commit = repo.latest_commit() repo.add_commit() repo.add_tag("tag") ref = "tag-0-g" + repo.latest_commit() element = {"kind": "import", "sources": [repo.source_config(ref=ref)]} generate_element(project, "target.bst", element) result = cli.run(project=project, args=["source", "fetch", "target.bst"]) result.assert_success() result = cli.run(project=project, args=["workspace", "open", "--directory", workspacedir, "target.bst"]) result.assert_success() assert subprocess.call(["git", "show", "tag"], cwd=workspacedir) == 0 assert subprocess.call(["git", "show", first_commit], cwd=workspacedir) != 0 @pytest.mark.skipif(HAVE_GIT is False, reason="git is not available") @pytest.mark.datafiles(os.path.join(DATA_DIR, "template")) def test_submodule_fetch_checkout(cli, tmpdir, datafiles): project = str(datafiles) checkoutdir = os.path.join(str(tmpdir), "checkout") # Create the submodule first from the 'subrepofiles' subdir subrepo = create_repo("git", str(tmpdir), "subrepo") subrepo.create(os.path.join(project, "subrepofiles")) # Create the repo from 'repofiles' subdir repo = create_repo("git", str(tmpdir)) repo.create(os.path.join(project, "repofiles")) # Add a submodule pointing to the one we created ref = repo.add_submodule("subdir", "file://" + subrepo.repo) # Write out our test target element = {"kind": "import", "sources": [repo.source_config(ref=ref)]} generate_element(project, "target.bst", element) # Fetch, build, checkout result = cli.run(project=project, args=["source", "fetch", "target.bst"]) result.assert_success() result = cli.run(project=project, args=["build", "target.bst"]) result.assert_success() result = cli.run(project=project, args=["artifact", "checkout", "target.bst", "--directory", checkoutdir]) result.assert_success() # Assert we checked out both files at their expected location assert os.path.exists(os.path.join(checkoutdir, "file.txt")) assert os.path.exists(os.path.join(checkoutdir, "subdir", "ponyfile.txt")) @pytest.mark.skipif(HAVE_GIT is False, reason="git is not available") @pytest.mark.datafiles(os.path.join(DATA_DIR, "template")) def test_recursive_submodule_fetch_checkout(cli, tmpdir, datafiles): project = str(datafiles) checkoutdir = os.path.join(str(tmpdir), "checkout") # Create a submodule from the 'othersubrepofiles' subdir subsubrepo = create_repo("git", str(tmpdir), "subsubrepo") subsubrepo.create(os.path.join(project, "othersubrepofiles")) # Create another submodule from the 'subrepofiles' subdir subrepo = create_repo("git", str(tmpdir), "subrepo") subrepo.create(os.path.join(project, "subrepofiles")) # Create the repo from 'repofiles' subdir repo = create_repo("git", str(tmpdir)) repo.create(os.path.join(project, "repofiles")) # Configure submodules subrepo.add_submodule("subdir", "file://" + subsubrepo.repo) ref = repo.add_submodule("subdir", "file://" + subrepo.repo) # Write out our test target element = {"kind": "import", "sources": [repo.source_config(ref=ref)]} generate_element(project, "target.bst", element) # Fetch, build, checkout result = cli.run(project=project, args=["source", "fetch", "target.bst"]) result.assert_success() result = cli.run(project=project, args=["build", "target.bst"]) result.assert_success() result = cli.run(project=project, args=["artifact", "checkout", "target.bst", "--directory", checkoutdir]) result.assert_success() # Assert we checked out all files at their expected location assert os.path.exists(os.path.join(checkoutdir, "file.txt")) assert os.path.exists(os.path.join(checkoutdir, "subdir", "ponyfile.txt")) assert os.path.exists(os.path.join(checkoutdir, "subdir", "subdir", "unicornfile.txt")) @pytest.mark.skipif(HAVE_GIT is False, reason="git is not available") @pytest.mark.datafiles(os.path.join(DATA_DIR, "template")) def test_submodule_fetch_source_enable_explicit(cli, tmpdir, datafiles): project = str(datafiles) checkoutdir = os.path.join(str(tmpdir), "checkout") # Create the submodule first from the 'subrepofiles' subdir subrepo = create_repo("git", str(tmpdir), "subrepo") subrepo.create(os.path.join(project, "subrepofiles")) # Create the repo from 'repofiles' subdir repo = create_repo("git", str(tmpdir)) repo.create(os.path.join(project, "repofiles")) # Add a submodule pointing to the one we created ref = repo.add_submodule("subdir", "file://" + subrepo.repo) # Write out our test target element = {"kind": "import", "sources": [repo.source_config_extra(ref=ref, checkout_submodules=True)]} generate_element(project, "target.bst", element) # Fetch, build, checkout result = cli.run(project=project, args=["source", "fetch", "target.bst"]) result.assert_success() result = cli.run(project=project, args=["build", "target.bst"]) result.assert_success() result = cli.run(project=project, args=["artifact", "checkout", "target.bst", "--directory", checkoutdir]) result.assert_success() # Assert we checked out both files at their expected location assert os.path.exists(os.path.join(checkoutdir, "file.txt")) assert os.path.exists(os.path.join(checkoutdir, "subdir", "ponyfile.txt")) @pytest.mark.skipif(HAVE_GIT is False, reason="git is not available") @pytest.mark.datafiles(os.path.join(DATA_DIR, "template")) def test_submodule_fetch_source_disable(cli, tmpdir, datafiles): project = str(datafiles) checkoutdir = os.path.join(str(tmpdir), "checkout") # Create the submodule first from the 'subrepofiles' subdir subrepo = create_repo("git", str(tmpdir), "subrepo") subrepo.create(os.path.join(project, "subrepofiles")) # Create the repo from 'repofiles' subdir repo = create_repo("git", str(tmpdir)) repo.create(os.path.join(project, "repofiles")) # Add a submodule pointing to the one we created ref = repo.add_submodule("subdir", "file://" + subrepo.repo) # Write out our test target element = {"kind": "import", "sources": [repo.source_config_extra(ref=ref, checkout_submodules=False)]} generate_element(project, "target.bst", element) # Fetch, build, checkout result = cli.run(project=project, args=["source", "fetch", "target.bst"]) result.assert_success() result = cli.run(project=project, args=["build", "target.bst"]) result.assert_success() result = cli.run(project=project, args=["artifact", "checkout", "target.bst", "--directory", checkoutdir]) result.assert_success() # Assert we checked out both files at their expected location assert os.path.exists(os.path.join(checkoutdir, "file.txt")) assert not os.path.exists(os.path.join(checkoutdir, "subdir", "ponyfile.txt")) @pytest.mark.skipif(HAVE_GIT is False, reason="git is not available") @pytest.mark.datafiles(os.path.join(DATA_DIR, "template")) def test_submodule_fetch_submodule_does_override(cli, tmpdir, datafiles): project = str(datafiles) checkoutdir = os.path.join(str(tmpdir), "checkout") # Create the submodule first from the 'subrepofiles' subdir subrepo = create_repo("git", str(tmpdir), "subrepo") subrepo.create(os.path.join(project, "subrepofiles")) # Create the repo from 'repofiles' subdir repo = create_repo("git", str(tmpdir)) repo.create(os.path.join(project, "repofiles")) # Add a submodule pointing to the one we created ref = repo.add_submodule("subdir", "file://" + subrepo.repo, checkout=True) # Write out our test target element = {"kind": "import", "sources": [repo.source_config_extra(ref=ref, checkout_submodules=False)]} generate_element(project, "target.bst", element) # Fetch, build, checkout result = cli.run(project=project, args=["source", "fetch", "target.bst"]) result.assert_success() result = cli.run(project=project, args=["build", "target.bst"]) result.assert_success() result = cli.run(project=project, args=["artifact", "checkout", "target.bst", "--directory", checkoutdir]) result.assert_success() # Assert we checked out both files at their expected location assert os.path.exists(os.path.join(checkoutdir, "file.txt")) assert os.path.exists(os.path.join(checkoutdir, "subdir", "ponyfile.txt")) @pytest.mark.skipif(HAVE_GIT is False, reason="git is not available") @pytest.mark.datafiles(os.path.join(DATA_DIR, "template")) def test_submodule_fetch_submodule_individual_checkout(cli, tmpdir, datafiles): project = str(datafiles) checkoutdir = os.path.join(str(tmpdir), "checkout") # Create the submodule first from the 'subrepofiles' subdir subrepo = create_repo("git", str(tmpdir), "subrepo") subrepo.create(os.path.join(project, "subrepofiles")) # Create another submodule from the 'othersubrepofiles' subdir other_subrepo = create_repo("git", str(tmpdir), "othersubrepo") other_subrepo.create(os.path.join(project, "othersubrepofiles")) # Create the repo from 'repofiles' subdir repo = create_repo("git", str(tmpdir)) repo.create(os.path.join(project, "repofiles")) # Add a submodule pointing to the one we created repo.add_submodule("subdir", "file://" + subrepo.repo, checkout=False) ref = repo.add_submodule("othersubdir", "file://" + other_subrepo.repo) # Write out our test target element = {"kind": "import", "sources": [repo.source_config_extra(ref=ref, checkout_submodules=True)]} generate_element(project, "target.bst", element) # Fetch, build, checkout result = cli.run(project=project, args=["source", "fetch", "target.bst"]) result.assert_success() result = cli.run(project=project, args=["build", "target.bst"]) result.assert_success() result = cli.run(project=project, args=["artifact", "checkout", "target.bst", "--directory", checkoutdir]) result.assert_success() # Assert we checked out files at their expected location assert os.path.exists(os.path.join(checkoutdir, "file.txt")) assert not os.path.exists(os.path.join(checkoutdir, "subdir", "ponyfile.txt")) assert os.path.exists(os.path.join(checkoutdir, "othersubdir", "unicornfile.txt")) @pytest.mark.skipif(HAVE_GIT is False, reason="git is not available") @pytest.mark.datafiles(os.path.join(DATA_DIR, "template")) def test_submodule_fetch_submodule_individual_checkout_explicit(cli, tmpdir, datafiles): project = str(datafiles) checkoutdir = os.path.join(str(tmpdir), "checkout") # Create the submodule first from the 'subrepofiles' subdir subrepo = create_repo("git", str(tmpdir), "subrepo") subrepo.create(os.path.join(project, "subrepofiles")) # Create another submodule from the 'othersubrepofiles' subdir other_subrepo = create_repo("git", str(tmpdir), "othersubrepo") other_subrepo.create(os.path.join(project, "othersubrepofiles")) # Create the repo from 'repofiles' subdir repo = create_repo("git", str(tmpdir)) repo.create(os.path.join(project, "repofiles")) # Add a submodule pointing to the one we created repo.add_submodule("subdir", "file://" + subrepo.repo, checkout=False) ref = repo.add_submodule("othersubdir", "file://" + other_subrepo.repo, checkout=True) # Write out our test target element = {"kind": "import", "sources": [repo.source_config_extra(ref=ref, checkout_submodules=True)]} generate_element(project, "target.bst", element) # Fetch, build, checkout result = cli.run(project=project, args=["source", "fetch", "target.bst"]) result.assert_success() result = cli.run(project=project, args=["build", "target.bst"]) result.assert_success() result = cli.run(project=project, args=["artifact", "checkout", "target.bst", "--directory", checkoutdir]) result.assert_success() # Assert we checked out files at their expected location assert os.path.exists(os.path.join(checkoutdir, "file.txt")) assert not os.path.exists(os.path.join(checkoutdir, "subdir", "ponyfile.txt")) assert os.path.exists(os.path.join(checkoutdir, "othersubdir", "unicornfile.txt")) @pytest.mark.skipif(HAVE_GIT is False, reason="git is not available") @pytest.mark.datafiles(os.path.join(DATA_DIR, "project-override")) def test_submodule_fetch_project_override(cli, tmpdir, datafiles): project = str(datafiles) checkoutdir = os.path.join(str(tmpdir), "checkout") # Create the submodule first from the 'subrepofiles' subdir subrepo = create_repo("git", str(tmpdir), "subrepo") subrepo.create(os.path.join(project, "subrepofiles")) # Create the repo from 'repofiles' subdir repo = create_repo("git", str(tmpdir)) repo.create(os.path.join(project, "repofiles")) # Add a submodule pointing to the one we created ref = repo.add_submodule("subdir", "file://" + subrepo.repo) # Write out our test target element = {"kind": "import", "sources": [repo.source_config(ref=ref)]} generate_element(project, "target.bst", element) # Fetch, build, checkout result = cli.run(project=project, args=["source", "fetch", "target.bst"]) result.assert_success() result = cli.run(project=project, args=["build", "target.bst"]) result.assert_success() result = cli.run(project=project, args=["artifact", "checkout", "target.bst", "--directory", checkoutdir]) result.assert_success() # Assert we checked out both files at their expected location assert os.path.exists(os.path.join(checkoutdir, "file.txt")) assert not os.path.exists(os.path.join(checkoutdir, "subdir", "ponyfile.txt")) @pytest.mark.skipif(HAVE_GIT is False, reason="git is not available") @pytest.mark.datafiles(os.path.join(DATA_DIR, "template")) def test_submodule_track_ignore_inconsistent(cli, tmpdir, datafiles): project = str(datafiles) # Create the repo from 'repofiles' subdir repo = create_repo("git", str(tmpdir)) ref = repo.create(os.path.join(project, "repofiles")) # Write out our test target element = {"kind": "import", "sources": [repo.source_config(ref=ref)]} generate_element(project, "target.bst", element) # Now add a .gitmodules file with an inconsistent submodule, # we are calling this inconsistent because the file was created # but `git submodule add` was never called, so there is no reference # associated to the submodule. # repo.add_file(os.path.join(project, "inconsistent-submodule", ".gitmodules")) # Fetch should work, we're not yet at the offending ref result = cli.run(project=project, args=["source", "fetch", "target.bst"]) result.assert_success() # Track to update to the offending commit result = cli.run(project=project, args=["source", "track", "target.bst"]) result.assert_success() # Fetch after track will encounter an inconsistent submodule without any ref result = cli.run(project=project, args=["source", "fetch", "target.bst"]) result.assert_success() # Assert that we are just fine without it, and emit a warning to the user. assert "Ignoring inconsistent submodule" in result.stderr @pytest.mark.skipif(HAVE_GIT is False, reason="git is not available") @pytest.mark.datafiles(os.path.join(DATA_DIR, "template")) def test_submodule_track_no_ref_or_track(cli, tmpdir, datafiles): project = str(datafiles) # Create the repo from 'repofiles' subdir repo = create_repo("git", str(tmpdir)) repo.create(os.path.join(project, "repofiles")) # Write out our test target gitsource = repo.source_config(ref=None) gitsource.pop("track") element = {"kind": "import", "sources": [gitsource]} generate_element(project, "target.bst", element) # Track will encounter an inconsistent submodule without any ref result = cli.run(project=project, args=["show", "target.bst"]) result.assert_main_error(ErrorDomain.SOURCE, "missing-track-and-ref") result.assert_task_error(None, None) @pytest.mark.skipif(HAVE_GIT is False, reason="git is not available") @pytest.mark.datafiles(os.path.join(DATA_DIR, "template")) @pytest.mark.parametrize("fail", ["warn", "error"]) def test_ref_not_in_track(cli, tmpdir, datafiles, fail): project = str(datafiles) # Make the warning an error if we're testing errors if fail == "error": generate_project_with_git(project, config={"fatal-warnings": [CoreWarnings.REF_NOT_IN_TRACK]}) # Create the repo from 'repofiles', create a branch without latest commit repo = create_repo("git", str(tmpdir)) ref = repo.create(os.path.join(project, "repofiles")) gitsource = repo.source_config(ref=ref) # Overwrite the track value to the added branch gitsource["track"] = "foo" # Write out our test target element = {"kind": "import", "sources": [gitsource]} generate_element(project, "target.bst", element) result = cli.run(project=project, args=["build", "target.bst"]) # Assert a warning or an error depending on what we're checking if fail == "error": result.assert_main_error(ErrorDomain.STREAM, None) result.assert_task_error(ErrorDomain.PLUGIN, CoreWarnings.REF_NOT_IN_TRACK) else: result.assert_success() assert "ref-not-in-track" in result.stderr @pytest.mark.skipif(HAVE_GIT is False, reason="git is not available") @pytest.mark.datafiles(os.path.join(DATA_DIR, "template")) @pytest.mark.parametrize("fail", ["warn", "error"]) def test_unlisted_submodule(cli, tmpdir, datafiles, fail): project = str(datafiles) # Make the warning an error if we're testing errors if fail == "error": generate_project_with_git(project, config={"fatal-warnings": ["git:unlisted-submodule"]}) # Create the submodule first from the 'subrepofiles' subdir subrepo = create_repo("git", str(tmpdir), "subrepo") subrepo.create(os.path.join(project, "subrepofiles")) # Create the repo from 'repofiles' subdir repo = create_repo("git", str(tmpdir)) repo.create(os.path.join(project, "repofiles")) # Add a submodule pointing to the one we created ref = repo.add_submodule("subdir", "file://" + subrepo.repo) # Create the source, and delete the explicit configuration # of the submodules. # # We expect this to cause an unlisted submodule warning # after the source has been fetched. # gitsource = repo.source_config(ref=ref) del gitsource["submodules"] # Write out our test target element = {"kind": "import", "sources": [gitsource]} generate_element(project, "target.bst", element) # The warning or error is reported during fetch. There should be no # error with `bst show`. result = cli.run(project=project, args=["show", "target.bst"]) result.assert_success() assert "git:unlisted-submodule" not in result.stderr # We will notice this directly in fetch, as it will try to fetch # the submodules it discovers as a result of fetching the primary repo. result = cli.run(project=project, args=["source", "fetch", "target.bst"]) # Assert a warning or an error depending on what we're checking if fail == "error": result.assert_main_error(ErrorDomain.STREAM, None) result.assert_task_error(ErrorDomain.PLUGIN, "git:unlisted-submodule") else: result.assert_success() assert "git:unlisted-submodule" in result.stderr # Verify that `bst show` will still not error out after fetching. result = cli.run(project=project, args=["show", "target.bst"]) result.assert_success() assert "git:unlisted-submodule" not in result.stderr @pytest.mark.skipif(HAVE_GIT is False, reason="git is not available") @pytest.mark.datafiles(os.path.join(DATA_DIR, "template")) @pytest.mark.parametrize("fail", ["warn", "error"]) def test_track_unlisted_submodule(cli, tmpdir, datafiles, fail): project = str(datafiles) # Make the warning an error if we're testing errors if fail == "error": generate_project_with_git(project, config={"fatal-warnings": ["git:unlisted-submodule"]}) # Create the submodule first from the 'subrepofiles' subdir subrepo = create_repo("git", str(tmpdir), "subrepo") subrepo.create(os.path.join(project, "subrepofiles")) # Create the repo from 'repofiles' subdir repo = create_repo("git", str(tmpdir)) ref = repo.create(os.path.join(project, "repofiles")) # Add a submodule pointing to the one we created, but use # the original ref, let the submodules appear after tracking repo.add_submodule("subdir", "file://" + subrepo.repo) # Create the source, and delete the explicit configuration # of the submodules. gitsource = repo.source_config(ref=ref) del gitsource["submodules"] # Write out our test target element = {"kind": "import", "sources": [gitsource]} generate_element(project, "target.bst", element) # Fetch the repo, we will not see the warning because we # are still pointing to a ref which predates the submodules result = cli.run(project=project, args=["source", "fetch", "target.bst"]) result.assert_success() assert "git:unlisted-submodule" not in result.stderr # We won't get a warning/error when tracking either, the source # has not become cached so the opportunity to check # for the warning has not yet arisen. result = cli.run(project=project, args=["source", "track", "target.bst"]) result.assert_success() assert "git:unlisted-submodule" not in result.stderr # Fetching the repo at the new ref will finally reveal the warning result = cli.run(project=project, args=["source", "fetch", "target.bst"]) if fail == "error": result.assert_main_error(ErrorDomain.STREAM, None) result.assert_task_error(ErrorDomain.PLUGIN, "git:unlisted-submodule") else: result.assert_success() assert "git:unlisted-submodule" in result.stderr @pytest.mark.skipif(HAVE_GIT is False, reason="git is not available") @pytest.mark.datafiles(os.path.join(DATA_DIR, "template")) @pytest.mark.parametrize("fail", ["warn", "error"]) def test_invalid_submodule(cli, tmpdir, datafiles, fail): project = str(datafiles) # Make the warning an error if we're testing errors if fail == "error": generate_project_with_git(project, config={"fatal-warnings": ["git:invalid-submodule"]}) # Create the repo from 'repofiles' subdir repo = create_repo("git", str(tmpdir)) ref = repo.create(os.path.join(project, "repofiles")) # Create the source without any submodules, and add # an invalid submodule configuration to it. # # We expect this to cause an invalid submodule warning # after the source has been fetched and we know what # the real submodules actually are. # gitsource = repo.source_config(ref=ref) gitsource["submodules"] = {"subdir": {"url": "https://pony.org/repo.git"}} # Write out our test target element = {"kind": "import", "sources": [gitsource]} generate_element(project, "target.bst", element) # The warning or error is reported during fetch. There should be no # error with `bst show`. result = cli.run(project=project, args=["show", "target.bst"]) result.assert_success() assert "git:invalid-submodule" not in result.stderr # We will notice this directly in fetch, as it will try to fetch # the submodules it discovers as a result of fetching the primary repo. result = cli.run(project=project, args=["source", "fetch", "target.bst"]) # Assert a warning or an error depending on what we're checking if fail == "error": result.assert_main_error(ErrorDomain.STREAM, None) result.assert_task_error(ErrorDomain.PLUGIN, "git:invalid-submodule") else: result.assert_success() assert "git:invalid-submodule" in result.stderr # Verify that `bst show` will still not error out after fetching. result = cli.run(project=project, args=["show", "target.bst"]) result.assert_success() assert "git:invalid-submodule" not in result.stderr @pytest.mark.skipif(HAVE_GIT is False, reason="git is not available") @pytest.mark.skipif(HAVE_OLD_GIT, reason="old git rm does not update .gitmodules") @pytest.mark.datafiles(os.path.join(DATA_DIR, "template")) @pytest.mark.parametrize("fail", ["warn", "error"]) def test_track_invalid_submodule(cli, tmpdir, datafiles, fail): project = str(datafiles) # Make the warning an error if we're testing errors if fail == "error": generate_project_with_git(project, config={"fatal-warnings": ["git:invalid-submodule"]}) # Create the submodule first from the 'subrepofiles' subdir subrepo = create_repo("git", str(tmpdir), "subrepo") subrepo.create(os.path.join(project, "subrepofiles")) # Create the repo from 'repofiles' subdir repo = create_repo("git", str(tmpdir)) repo.create(os.path.join(project, "repofiles")) # Add a submodule pointing to the one we created ref = repo.add_submodule("subdir", "file://" + subrepo.repo) # Add a commit beyond the ref which *removes* the submodule we've added repo.remove_path("subdir") # Create the source, this will keep the submodules so initially # the configuration is valid for the ref we're using gitsource = repo.source_config(ref=ref) # Write out our test target element = {"kind": "import", "sources": [gitsource]} generate_element(project, "target.bst", element) # Fetch the repo, we will not see the warning because we # are still pointing to a ref which predates the submodules result = cli.run(project=project, args=["source", "fetch", "target.bst"]) result.assert_success() assert "git:invalid-submodule" not in result.stderr # After tracking we're pointing to a ref, which would trigger an invalid # submodule warning. However, cache validation is only performed as part # of fetch. result = cli.run(project=project, args=["source", "track", "target.bst"]) result.assert_success() # Fetch to trigger cache validation result = cli.run(project=project, args=["source", "fetch", "target.bst"]) if fail == "error": result.assert_main_error(ErrorDomain.STREAM, None) result.assert_task_error(ErrorDomain.PLUGIN, "git:invalid-submodule") else: result.assert_success() assert "git:invalid-submodule" in result.stderr @pytest.mark.skipif(HAVE_GIT is False, reason="git is not available") @pytest.mark.datafiles(os.path.join(DATA_DIR, "template")) @pytest.mark.parametrize("ref_format", ["sha1", "git-describe"]) @pytest.mark.parametrize("tag,extra_commit", [(False, False), (True, False), (True, True)]) def test_track_fetch(cli, tmpdir, datafiles, ref_format, tag, extra_commit): project = str(datafiles) # Create the repo from 'repofiles' subdir repo = create_repo("git", str(tmpdir)) repo.create(os.path.join(project, "repofiles")) if tag: repo.add_tag("tag") if extra_commit: repo.add_commit() # Write out our test target element = {"kind": "import", "sources": [repo.source_config()]} element["sources"][0]["ref-format"] = ref_format generate_element(project, "target.bst", element) element_path = os.path.join(project, "target.bst") # Track it result = cli.run(project=project, args=["source", "track", "target.bst"]) result.assert_success() element = load_yaml(element_path) new_ref = element.get_sequence("sources").mapping_at(0).get_str("ref") if ref_format == "git-describe" and tag: # Check and strip prefix prefix = "tag-{}-g".format(0 if not extra_commit else 1) assert new_ref.startswith(prefix) new_ref = new_ref[len(prefix) :] # 40 chars for SHA-1 assert len(new_ref) == 40 # Fetch it result = cli.run(project=project, args=["source", "fetch", "target.bst"]) result.assert_success() @pytest.mark.skipif(HAVE_GIT is False, reason="git is not available") @pytest.mark.skipif(HAVE_OLD_GIT, reason="old git describe lacks --first-parent") @pytest.mark.datafiles(os.path.join(DATA_DIR, "template")) @pytest.mark.parametrize("ref_storage", [("inline"), ("project.refs")]) @pytest.mark.parametrize("tag_type", [("annotated"), ("lightweight")]) def test_git_describe(cli, tmpdir, datafiles, ref_storage, tag_type): project = str(datafiles) project_config = load_yaml(os.path.join(project, "project.conf")) project_config["ref-storage"] = ref_storage generate_project_with_git(project, config=project_config) repofiles = os.path.join(str(tmpdir), "repofiles") os.makedirs(repofiles, exist_ok=True) file0 = os.path.join(repofiles, "file0") with open(file0, "w", encoding="utf-8") as f: f.write("test\n") repo = create_repo("git", str(tmpdir)) def tag(name): if tag_type == "annotated": repo.add_annotated_tag(name, name) else: repo.add_tag(name) repo.create(repofiles) tag("uselesstag") file1 = os.path.join(str(tmpdir), "file1") with open(file1, "w", encoding="utf-8") as f: f.write("test\n") repo.add_file(file1) tag("tag1") file2 = os.path.join(str(tmpdir), "file2") with open(file2, "w", encoding="utf-8") as f: f.write("test\n") repo.branch("branch2") repo.add_file(file2) tag("tag2") repo.checkout("master") file3 = os.path.join(str(tmpdir), "file3") with open(file3, "w", encoding="utf-8") as f: f.write("test\n") repo.add_file(file3) repo.merge("branch2") config = repo.source_config() config["track"] = repo.latest_commit() config["track-tags"] = True # Write out our test target element = { "kind": "import", "sources": [config], } generate_element(project, "target.bst", element) element_path = os.path.join(project, "target.bst") if ref_storage == "inline": result = cli.run(project=project, args=["source", "track", "target.bst"]) result.assert_success() else: result = cli.run(project=project, args=["source", "track", "target.bst", "--deps", "all"]) result.assert_success() if ref_storage == "inline": element = load_yaml(element_path) tags = element.get_sequence("sources").mapping_at(0).get_sequence("tags") assert len(tags) == 2 for tag in tags: assert "tag" in tag assert "commit" in tag assert "annotated" in tag assert tag.get_bool("annotated") == (tag_type == "annotated") assert {(tag.get_str("tag"), tag.get_str("commit")) for tag in tags} == { ("tag1", repo.rev_parse("tag1^{commit}")), ("tag2", repo.rev_parse("tag2^{commit}")), } checkout = os.path.join(str(tmpdir), "checkout") result = cli.run(project=project, args=["build", "target.bst"]) result.assert_success() result = cli.run(project=project, args=["artifact", "checkout", "target.bst", "--directory", checkout]) result.assert_success() if tag_type == "annotated": options = [] else: options = ["--tags"] describe = subprocess.check_output(["git", "describe", *options], cwd=checkout, universal_newlines=True) assert describe.startswith("tag2-2-") describe_fp = subprocess.check_output( ["git", "describe", "--first-parent", *options], cwd=checkout, universal_newlines=True ) assert describe_fp.startswith("tag1-2-") tags = subprocess.check_output(["git", "tag"], cwd=checkout, universal_newlines=True) tags = set(tags.splitlines()) assert tags == set(["tag1", "tag2"]) with pytest.raises(subprocess.CalledProcessError): subprocess.run(["git", "log", repo.rev_parse("uselesstag")], cwd=checkout, check=True) @pytest.mark.skipif(HAVE_GIT is False, reason="git is not available") @pytest.mark.datafiles(os.path.join(DATA_DIR, "template")) @pytest.mark.parametrize("ref_storage", [("inline"), ("project.refs")]) @pytest.mark.parametrize("tag_type", [("annotated"), ("lightweight")]) def test_git_describe_head_is_tagged(cli, tmpdir, datafiles, ref_storage, tag_type): project = str(datafiles) project_config = load_yaml(os.path.join(project, "project.conf")) project_config["ref-storage"] = ref_storage generate_project_with_git(project, config=project_config) repofiles = os.path.join(str(tmpdir), "repofiles") os.makedirs(repofiles, exist_ok=True) file0 = os.path.join(repofiles, "file0") with open(file0, "w", encoding="utf-8") as f: f.write("test\n") repo = create_repo("git", str(tmpdir)) def tag(name): if tag_type == "annotated": repo.add_annotated_tag(name, name) else: repo.add_tag(name) repo.create(repofiles) tag("uselesstag") file1 = os.path.join(str(tmpdir), "file1") with open(file1, "w", encoding="utf-8") as f: f.write("test\n") repo.add_file(file1) file2 = os.path.join(str(tmpdir), "file2") with open(file2, "w", encoding="utf-8") as f: f.write("test\n") repo.branch("branch2") repo.add_file(file2) repo.checkout("master") file3 = os.path.join(str(tmpdir), "file3") with open(file3, "w", encoding="utf-8") as f: f.write("test\n") repo.add_file(file3) tagged_ref = repo.merge("branch2") tag("tag") config = repo.source_config() config["track"] = repo.latest_commit() config["track-tags"] = True # Write out our test target element = { "kind": "import", "sources": [config], } generate_element(project, "target.bst", element) element_path = os.path.join(project, "target.bst") if ref_storage == "inline": result = cli.run(project=project, args=["source", "track", "target.bst"]) result.assert_success() else: result = cli.run(project=project, args=["source", "track", "target.bst", "--deps", "all"]) result.assert_success() if ref_storage == "inline": element = load_yaml(element_path) source = element.get_sequence("sources").mapping_at(0) tags = source.get_sequence("tags") assert len(tags) == 1 tag = source.get_sequence("tags").mapping_at(0) assert "tag" in tag assert "commit" in tag assert "annotated" in tag assert tag.get_bool("annotated") == (tag_type == "annotated") tag_name = tag.get_str("tag") commit = tag.get_str("commit") assert (tag_name, commit) == ("tag", repo.rev_parse("tag^{commit}")) checkout = os.path.join(str(tmpdir), "checkout") result = cli.run(project=project, args=["build", "target.bst"]) result.assert_success() result = cli.run(project=project, args=["artifact", "checkout", "target.bst", "--directory", checkout]) result.assert_success() if tag_type == "annotated": options = [] else: options = ["--tags"] describe = subprocess.check_output(["git", "describe", *options], cwd=checkout, universal_newlines=True) assert describe.startswith("tag") tags = subprocess.check_output(["git", "tag"], cwd=checkout, universal_newlines=True) tags = set(tags.splitlines()) assert tags == set(["tag"]) rev_list = subprocess.check_output(["git", "rev-list", "--all"], cwd=checkout, universal_newlines=True) assert set(rev_list.splitlines()) == set([tagged_ref]) with pytest.raises(subprocess.CalledProcessError): subprocess.run(["git", "log", repo.rev_parse("uselesstag")], cwd=checkout, check=True) @pytest.mark.skipif(HAVE_GIT is False, reason="git is not available") @pytest.mark.datafiles(os.path.join(DATA_DIR, "template")) def test_git_describe_relevant_history(cli, tmpdir, datafiles): project = str(datafiles) project_config = load_yaml(os.path.join(project, "project.conf")) project_config["ref-storage"] = "project.refs" generate_project_with_git(project, config=project_config) repofiles = os.path.join(str(tmpdir), "repofiles") os.makedirs(repofiles, exist_ok=True) file0 = os.path.join(repofiles, "file0") with open(file0, "w", encoding="utf-8") as f: f.write("test\n") repo = create_repo("git", str(tmpdir)) repo.create(repofiles) file1 = os.path.join(str(tmpdir), "file1") with open(file1, "w", encoding="utf-8") as f: f.write("test\n") repo.add_file(file1) repo.branch("branch") repo.checkout("master") file2 = os.path.join(str(tmpdir), "file2") with open(file2, "w", encoding="utf-8") as f: f.write("test\n") repo.add_file(file2) file3 = os.path.join(str(tmpdir), "file3") with open(file3, "w", encoding="utf-8") as f: f.write("test\n") branch_boundary = repo.add_file(file3) repo.checkout("branch") file4 = os.path.join(str(tmpdir), "file4") with open(file4, "w", encoding="utf-8") as f: f.write("test\n") tagged_ref = repo.add_file(file4) repo.add_annotated_tag("tag1", "tag1") head = repo.merge("master") config = repo.source_config() config["track"] = head config["track-tags"] = True # Write out our test target element = { "kind": "import", "sources": [config], } generate_element(project, "target.bst", element) result = cli.run(project=project, args=["source", "track", "target.bst", "--deps", "all"]) result.assert_success() checkout = os.path.join(str(tmpdir), "checkout") result = cli.run(project=project, args=["build", "target.bst"]) result.assert_success() result = cli.run(project=project, args=["artifact", "checkout", "target.bst", "--directory", checkout]) result.assert_success() describe = subprocess.check_output(["git", "describe"], cwd=checkout, universal_newlines=True) assert describe.startswith("tag1-2-") rev_list = subprocess.check_output(["git", "rev-list", "--all"], cwd=checkout, universal_newlines=True) assert set(rev_list.splitlines()) == set([head, tagged_ref, branch_boundary]) @pytest.mark.skipif(HAVE_GIT is False, reason="git is not available") @pytest.mark.datafiles(os.path.join(DATA_DIR, "template")) def test_default_do_not_track_tags(cli, tmpdir, datafiles): project = str(datafiles) project_config = load_yaml(os.path.join(project, "project.conf")) project_config["ref-storage"] = "inline" generate_project_with_git(project, config=project_config) repofiles = os.path.join(str(tmpdir), "repofiles") os.makedirs(repofiles, exist_ok=True) file0 = os.path.join(repofiles, "file0") with open(file0, "w", encoding="utf-8") as f: f.write("test\n") repo = create_repo("git", str(tmpdir)) repo.create(repofiles) repo.add_tag("tag") config = repo.source_config() config["track"] = repo.latest_commit() # Write out our test target element = { "kind": "import", "sources": [config], } generate_element(project, "target.bst", element) element_path = os.path.join(project, "target.bst") result = cli.run(project=project, args=["source", "track", "target.bst"]) result.assert_success() element = load_yaml(element_path) source = element.get_sequence("sources").mapping_at(0) assert "tags" not in source @pytest.mark.skipif(HAVE_GIT is False, reason="git is not available") @pytest.mark.datafiles(os.path.join(DATA_DIR, "template")) def test_overwrite_rogue_tag_multiple_remotes(cli, tmpdir, datafiles): """When using multiple remotes in cache (i.e. when using aliases), we need to make sure we override tags. This is not allowed to fetch tags that were present from different origins """ project = str(datafiles) repofiles = os.path.join(str(tmpdir), "repofiles") os.makedirs(repofiles, exist_ok=True) file0 = os.path.join(repofiles, "file0") with open(file0, "w", encoding="utf-8") as f: f.write("test\n") repo = create_repo("git", str(tmpdir)) top_commit = repo.create(repofiles) repodir, reponame = os.path.split(repo.repo) project_config = load_yaml(os.path.join(project, "project.conf")) project_config["aliases"] = Node.from_dict({"repo": "http://example.com/"}) project_config["mirrors"] = [{"name": "middle-earth", "aliases": {"repo": ["file://{}/".format(repodir)]}}] generate_project_with_git(project, config=project_config) repo.add_annotated_tag("tag", "tag") file1 = os.path.join(repofiles, "file1") with open(file1, "w", encoding="utf-8") as f: f.write("test\n") ref = repo.add_file(file1) config = repo.source_config(ref=ref) del config["track"] config["url"] = "repo:{}".format(reponame) # Write out our test target element = { "kind": "import", "sources": [config], } generate_element(project, "target.bst", element) result = cli.run(project=project, args=["build", "target.bst"]) result.assert_success() repo.checkout(top_commit) file2 = os.path.join(repofiles, "file2") with open(file2, "w", encoding="utf-8") as f: f.write("test\n") new_ref = repo.add_file(file2) repo.delete_tag("tag") repo.add_annotated_tag("tag", "tag") repo.checkout("master") otherpath = os.path.join(str(tmpdir), "other_path") shutil.copytree(repo.repo, os.path.join(otherpath, "repo")) create_repo("git", otherpath) repodir, reponame = os.path.split(repo.repo) generate_project_with_git(project, config=project_config) config = repo.source_config(ref=new_ref) del config["track"] config["url"] = "repo:{}".format(reponame) element = { "kind": "import", "sources": [config], } generate_element(project, "target.bst", element) result = cli.run(project=project, args=["build", "target.bst"]) result.assert_success() @pytest.mark.datafiles(os.path.join(DATA_DIR, "source-info")) @pytest.mark.parametrize( "target, expected_kind, expected_url, expected_medium, expected_version_type, expected_version, expected_guess_version, expected_offset", [ ( "no-describe.bst", "git", "https://flying-ponies.com/pony.git", "git", "commit", "40af18f96af0c7f1b56f67339ef53e000e738754", None, None, ), ( "describe.bst", "git", "https://flying-ponies.com/pony.git", "git", "commit", "40af18f96af0c7f1b56f67339ef53e000e738754", "3.4", None, ), ( "describe-offset.bst", "git", "https://flying-ponies.com/pony.git", "git", "commit", "40af18f96af0c7f1b56f67339ef53e000e738754", "3.4", "7", ), ( "describe-custom.bst", "git", "https://flying-ponies.com/pony.git", "git", "commit", "40af18f96af0c7f1b56f67339ef53e000e738754", "3.4.6", None, ), ( "override.bst", "git", "https://flying-ponies.com/pony.git", "git", "commit", "40af18f96af0c7f1b56f67339ef53e000e738754", "5.5", None, ), ], ids=["no-describe", "describe", "commit-offset", "custom-pattern", "override"], ) def test_source_info( cli, datafiles, target, expected_url, expected_kind, expected_medium, expected_version_type, expected_version, expected_guess_version, expected_offset, ): project = str(datafiles) result = cli.run(project=project, silent=True, args=["show", "--format", "%{name}:\n%{source-info}", target]) result.assert_success() loaded = _yaml.load_data(result.output) sources = loaded.get_sequence(target) source_info = sources.mapping_at(0) assert source_info.get_str("kind") == expected_kind assert source_info.get_str("url") == expected_url assert source_info.get_str("medium") == expected_medium assert source_info.get_str("version-type") == expected_version_type assert source_info.get_str("version") == expected_version guess_version = source_info.get_str("version-guess", None) if guess_version or expected_guess_version: assert guess_version == expected_guess_version extra_data = source_info.get_mapping("extra-data", None) if extra_data: commit_offset = extra_data.get_str("commit-offset", None) else: commit_offset = None assert commit_offset == expected_offset buildstream-plugins-2.7.0/tests/sources/git/000077500000000000000000000000001515254461100211265ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/git/project-override/000077500000000000000000000000001515254461100244115ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/git/project-override/project.conf000066400000000000000000000003741515254461100267320ustar00rootroot00000000000000# Basic project name: foo min-version: 2.0 plugins: - origin: pip package-name: buildstream-plugins sources: - git sources: git: config: checkout-submodules: False elements: manual: config: build-commands: - "foo" buildstream-plugins-2.7.0/tests/sources/git/project-override/repofiles/000077500000000000000000000000001515254461100264015ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/git/project-override/repofiles/file.txt000066400000000000000000000000051515254461100300540ustar00rootroot00000000000000pony buildstream-plugins-2.7.0/tests/sources/git/project-override/subrepofiles/000077500000000000000000000000001515254461100271135ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/git/project-override/subrepofiles/ponyfile.txt000066400000000000000000000000051515254461100314740ustar00rootroot00000000000000file buildstream-plugins-2.7.0/tests/sources/git/source-info/000077500000000000000000000000001515254461100233575ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/git/source-info/elements/000077500000000000000000000000001515254461100251735ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/git/source-info/elements/describe-custom.bst000066400000000000000000000002731515254461100307770ustar00rootroot00000000000000kind: import sources: - kind: git url: https://flying-ponies.com/pony.git ref: pony-3_4_r6-0-g40af18f96af0c7f1b56f67339ef53e000e738754 version-guess-pattern: '(\d+)_(\d+)_r(\d+)?' buildstream-plugins-2.7.0/tests/sources/git/source-info/elements/describe-offset.bst000066400000000000000000000002041515254461100307450ustar00rootroot00000000000000kind: import sources: - kind: git url: https://flying-ponies.com/pony.git ref: 3.4-7-g40af18f96af0c7f1b56f67339ef53e000e738754 buildstream-plugins-2.7.0/tests/sources/git/source-info/elements/describe.bst000066400000000000000000000002111515254461100274570ustar00rootroot00000000000000kind: import sources: - kind: git url: https://flying-ponies.com/pony.git ref: pony-3.4-0-g40af18f96af0c7f1b56f67339ef53e000e738754 buildstream-plugins-2.7.0/tests/sources/git/source-info/elements/no-describe.bst000066400000000000000000000001751515254461100301020ustar00rootroot00000000000000kind: import sources: - kind: git url: https://flying-ponies.com/pony.git ref: 40af18f96af0c7f1b56f67339ef53e000e738754 buildstream-plugins-2.7.0/tests/sources/git/source-info/elements/override.bst000066400000000000000000000002301515254461100275170ustar00rootroot00000000000000kind: import sources: - kind: git url: https://flying-ponies.com/pony.git ref: pony-3.4-0-g40af18f96af0c7f1b56f67339ef53e000e738754 version: 5.5 buildstream-plugins-2.7.0/tests/sources/git/source-info/project.conf000066400000000000000000000002611515254461100256730ustar00rootroot00000000000000# Project config for bst show source-info test name: test min-version: 2.5 element-path: elements plugins: - origin: pip package-name: buildstream-plugins sources: - git buildstream-plugins-2.7.0/tests/sources/git/template/000077500000000000000000000000001515254461100227415ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/git/template/inconsistent-submodule/000077500000000000000000000000001515254461100274565ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/git/template/inconsistent-submodule/.gitmodules000066400000000000000000000001001515254461100316220ustar00rootroot00000000000000[submodule "farm/pony"] path = farm/pony url = git://pony.com buildstream-plugins-2.7.0/tests/sources/git/template/othersubrepofiles/000077500000000000000000000000001515254461100265055ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/git/template/othersubrepofiles/unicornfile.txt000066400000000000000000000000051515254461100315560ustar00rootroot00000000000000file buildstream-plugins-2.7.0/tests/sources/git/template/project.conf000066400000000000000000000001721515254461100252560ustar00rootroot00000000000000# Basic project name: foo min-version: 2.0 plugins: - origin: pip package-name: buildstream-plugins sources: - git buildstream-plugins-2.7.0/tests/sources/git/template/repofiles/000077500000000000000000000000001515254461100247315ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/git/template/repofiles/file.txt000066400000000000000000000000051515254461100264040ustar00rootroot00000000000000pony buildstream-plugins-2.7.0/tests/sources/git/template/subrepofiles/000077500000000000000000000000001515254461100254435ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/git/template/subrepofiles/ponyfile.txt000066400000000000000000000000051515254461100300240ustar00rootroot00000000000000file buildstream-plugins-2.7.0/tests/sources/patch.py000066400000000000000000000202361515254461100220170ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Pylint doesn't play well with fixtures and dependency injection from pytest # pylint: disable=redefined-outer-name import os import socket import pytest from buildstream import _yaml from buildstream.exceptions import ErrorDomain, LoadErrorReason from buildstream._testing import cli # pylint: disable=unused-import DATA_DIR = os.path.join( os.path.dirname(os.path.realpath(__file__)), "patch", ) # generate_file_types() # # Generator that creates a regular file directory, symbolic link, fifo # and socket at the specified path. # # Args: # path: (str) path where to create each different type of file # def generate_file_types(path): def clean(): if os.path.exists(path): if os.path.isdir(path): os.rmdir(path) else: os.remove(path) clean() with open(path, "w", encoding="utf-8"): pass yield clean() os.makedirs(path) yield clean() os.symlink("project.conf", path) yield clean() os.mkfifo(path) yield clean() # Change directory because the full path may be longer than the ~100 # characters permitted for a unix socket old_dir = os.getcwd() parent, child = os.path.split(path) os.chdir(parent) s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) try: s.bind(child) os.chdir(old_dir) yield finally: s.close() clean() @pytest.mark.datafiles(os.path.join(DATA_DIR, "basic")) def test_missing_patch(cli, datafiles): project = str(datafiles) # Removing the local file causes preflight to fail localfile = os.path.join(project, "file_1.patch") os.remove(localfile) result = cli.run(project=project, args=["show", "target.bst"]) result.assert_main_error(ErrorDomain.LOAD, LoadErrorReason.MISSING_FILE) @pytest.mark.datafiles(os.path.join(DATA_DIR, "basic")) def test_non_regular_file_patch(cli, datafiles): project = str(datafiles) patch_path = os.path.join(project, "irregular_file.patch") for _file_type in generate_file_types(patch_path): result = cli.run(project=project, args=["show", "irregular.bst"]) if os.path.isfile(patch_path) and not os.path.islink(patch_path): result.assert_success() else: result.assert_main_error(ErrorDomain.LOAD, LoadErrorReason.PROJ_PATH_INVALID_KIND) @pytest.mark.datafiles(os.path.join(DATA_DIR, "basic")) def test_invalid_absolute_path(cli, datafiles): project = str(datafiles) with open(os.path.join(project, "target.bst"), "r", encoding="utf-8") as f: old_yaml = f.read() new_yaml = old_yaml.replace("file_1.patch", os.path.join(project, "file_1.patch")) assert old_yaml != new_yaml with open(os.path.join(project, "target.bst"), "w", encoding="utf-8") as f: f.write(new_yaml) result = cli.run(project=project, args=["show", "target.bst"]) result.assert_main_error(ErrorDomain.LOAD, LoadErrorReason.PROJ_PATH_INVALID) @pytest.mark.datafiles(os.path.join(DATA_DIR, "invalid-relative-path")) def test_invalid_relative_path(cli, datafiles): project = str(datafiles) result = cli.run(project=project, args=["show", "irregular.bst"]) result.assert_main_error(ErrorDomain.LOAD, LoadErrorReason.PROJ_PATH_INVALID) @pytest.mark.datafiles(os.path.join(DATA_DIR, "basic")) def test_stage_and_patch(cli, tmpdir, datafiles): project = str(datafiles) checkoutdir = os.path.join(str(tmpdir), "checkout") # Build, checkout result = cli.run(project=project, args=["build", "target.bst"]) result.assert_success() result = cli.run(project=project, args=["artifact", "checkout", "target.bst", "--directory", checkoutdir]) result.assert_success() # Test the file.txt was patched and changed with open(os.path.join(checkoutdir, "file.txt"), encoding="utf-8") as f: assert f.read() == "This is text file with superpowers\n" @pytest.mark.datafiles(os.path.join(DATA_DIR, "basic")) def test_stage_file_nonexistent_dir(cli, datafiles): project = str(datafiles) # Fails at build time because it tries to patch into a non-existing directory result = cli.run(project=project, args=["build", "failure-nonexistent-dir.bst"]) result.assert_main_error(ErrorDomain.STREAM, None) result.assert_task_error(ErrorDomain.SOURCE, "patch-no-files") @pytest.mark.datafiles(os.path.join(DATA_DIR, "basic")) def test_stage_file_empty_dir(cli, datafiles): project = str(datafiles) # Fails at build time because it tries to patch with nothing else staged result = cli.run(project=project, args=["build", "failure-empty-dir.bst"]) result.assert_main_error(ErrorDomain.STREAM, None) result.assert_task_error(ErrorDomain.SOURCE, "patch-no-files") @pytest.mark.datafiles(os.path.join(DATA_DIR, "separate-patch-dir")) def test_stage_separate_patch_dir(cli, tmpdir, datafiles): project = str(datafiles) checkoutdir = os.path.join(str(tmpdir), "checkout") # Track, fetch, build, checkout result = cli.run(project=project, args=["build", "target.bst"]) result.assert_success() result = cli.run(project=project, args=["artifact", "checkout", "target.bst", "--directory", checkoutdir]) result.assert_success() # Test the file.txt was patched and changed with open(os.path.join(checkoutdir, "test-dir", "file.txt"), encoding="utf-8") as f: assert f.read() == "This is text file in a directory with superpowers\n" @pytest.mark.datafiles(os.path.join(DATA_DIR, "multiple-patches")) def test_stage_multiple_patches(cli, tmpdir, datafiles): project = str(datafiles) checkoutdir = os.path.join(str(tmpdir), "checkout") # Track, fetch, build, checkout result = cli.run(project=project, args=["build", "target.bst"]) result.assert_success() result = cli.run(project=project, args=["artifact", "checkout", "target.bst", "--directory", checkoutdir]) result.assert_success() # Test the file.txt was patched and changed with open(os.path.join(checkoutdir, "file.txt"), encoding="utf-8") as f: assert f.read() == "This is text file with more superpowers\n" @pytest.mark.datafiles(os.path.join(DATA_DIR, "different-strip-level")) def test_patch_strip_level(cli, tmpdir, datafiles): project = str(datafiles) checkoutdir = os.path.join(str(tmpdir), "checkout") # Track, fetch, build, checkout result = cli.run(project=project, args=["build", "target.bst"]) result.assert_success() result = cli.run(project=project, args=["artifact", "checkout", "target.bst", "--directory", checkoutdir]) result.assert_success() # Test the file.txt was patched and changed with open(os.path.join(checkoutdir, "file.txt"), encoding="utf-8") as f: assert f.read() == "This is text file with superpowers\n" @pytest.mark.datafiles(os.path.join(DATA_DIR, "basic")) def test_show_source_info(cli, tmpdir, datafiles): # Get the source info project = str(datafiles) result = cli.run(project=project, args=["show", "--format", "%{name}:\n%{source-info}", "target.bst"]) result.assert_success() # Check the results of the patch source, which will be second in the list after the local file loaded = _yaml.load_data(result.output) sources = loaded.get_sequence("target.bst") source_info = sources.mapping_at(1) assert source_info.get_str("kind") == "patch" assert source_info.get_str("url") == "file_1.patch" assert source_info.get_str("medium") == "local" assert source_info.get_str("version-type") == "sha256" assert source_info.get_str("version") == "063fbc138d30a2f2ea8f3cd53810824520ff3971d752235c70f17c67fefca0f1" assert source_info.get_str("version-guess", None) is None buildstream-plugins-2.7.0/tests/sources/patch/000077500000000000000000000000001515254461100214425ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/patch/basic/000077500000000000000000000000001515254461100225235ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/patch/basic/failure-empty-dir.bst000066400000000000000000000001341515254461100265720ustar00rootroot00000000000000kind: import description: This is also the pony sources: - kind: patch path: file_1.patch buildstream-plugins-2.7.0/tests/sources/patch/basic/failure-nonexistent-dir.bst000066400000000000000000000001651515254461100300160ustar00rootroot00000000000000kind: import description: This is also the pony sources: - kind: patch path: file_1.patch directory: /idontexist buildstream-plugins-2.7.0/tests/sources/patch/basic/file.txt000066400000000000000000000000241515254461100241770ustar00rootroot00000000000000This is a text file buildstream-plugins-2.7.0/tests/sources/patch/basic/file_1.patch000066400000000000000000000002421515254461100247010ustar00rootroot00000000000000diff --git a/file.txt b/file.txt index a496efe..341ef26 100644 --- a/file.txt +++ b/file.txt @@ -1 +1 @@ -This is a text file +This is text file with superpowers buildstream-plugins-2.7.0/tests/sources/patch/basic/irregular.bst000066400000000000000000000001761515254461100252350ustar00rootroot00000000000000kind: import description: This is the pony sources: - kind: local path: file.txt - kind: patch path: irregular_file.patch buildstream-plugins-2.7.0/tests/sources/patch/basic/project.conf000066400000000000000000000001741515254461100250420ustar00rootroot00000000000000# Basic project name: foo min-version: 2.0 plugins: - origin: pip package-name: buildstream-plugins sources: - patch buildstream-plugins-2.7.0/tests/sources/patch/basic/target.bst000066400000000000000000000001661515254461100245260ustar00rootroot00000000000000kind: import description: This is the pony sources: - kind: local path: file.txt - kind: patch path: file_1.patch buildstream-plugins-2.7.0/tests/sources/patch/different-strip-level/000077500000000000000000000000001515254461100256545ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/patch/different-strip-level/file.txt000066400000000000000000000000241515254461100273300ustar00rootroot00000000000000This is a text file buildstream-plugins-2.7.0/tests/sources/patch/different-strip-level/file_1.patch000066400000000000000000000002621515254461100300340ustar00rootroot00000000000000diff --git foo/a/file.txt foo/b/file.txt index a496efe..341ef26 100644 --- foo/a/file.txt +++ foo/b/file.txt @@ -1 +1 @@ -This is a text file +This is text file with superpowers buildstream-plugins-2.7.0/tests/sources/patch/different-strip-level/project.conf000066400000000000000000000001741515254461100301730ustar00rootroot00000000000000# Basic project name: foo min-version: 2.0 plugins: - origin: pip package-name: buildstream-plugins sources: - patch buildstream-plugins-2.7.0/tests/sources/patch/different-strip-level/target.bst000066400000000000000000000002071515254461100276530ustar00rootroot00000000000000kind: import description: This is the pony sources: - kind: local path: file.txt - kind: patch path: file_1.patch strip-level: 2 buildstream-plugins-2.7.0/tests/sources/patch/invalid-relative-path/000077500000000000000000000000001515254461100256335ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/patch/invalid-relative-path/file_1.patch000066400000000000000000000002421515254461100300110ustar00rootroot00000000000000diff --git a/file.txt b/file.txt index a496efe..341ef26 100644 --- a/file.txt +++ b/file.txt @@ -1 +1 @@ -This is a text file +This is text file with superpowers buildstream-plugins-2.7.0/tests/sources/patch/invalid-relative-path/irregular.bst000066400000000000000000000001701515254461100303370ustar00rootroot00000000000000kind: import description: This is the pony sources: - kind: patch path: ../invalid-relative-path/irregular_file.patch buildstream-plugins-2.7.0/tests/sources/patch/invalid-relative-path/project.conf000066400000000000000000000001741515254461100301520ustar00rootroot00000000000000# Basic project name: foo min-version: 2.0 plugins: - origin: pip package-name: buildstream-plugins sources: - patch buildstream-plugins-2.7.0/tests/sources/patch/multiple-patches/000077500000000000000000000000001515254461100247225ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/patch/multiple-patches/file.txt000066400000000000000000000000241515254461100263760ustar00rootroot00000000000000This is a text file buildstream-plugins-2.7.0/tests/sources/patch/multiple-patches/file_1.patch000066400000000000000000000002421515254461100271000ustar00rootroot00000000000000diff --git a/file.txt b/file.txt index a496efe..341ef26 100644 --- a/file.txt +++ b/file.txt @@ -1 +1 @@ -This is a text file +This is text file with superpowers buildstream-plugins-2.7.0/tests/sources/patch/multiple-patches/file_2.patch000066400000000000000000000002661515254461100271070ustar00rootroot00000000000000diff --git a/file.txt b/file.txt index a496efe..341ef26 100644 --- a/file.txt +++ b/file.txt @@ -1 +1 @@ -This is text file with superpowers +This is text file with more superpowers buildstream-plugins-2.7.0/tests/sources/patch/multiple-patches/project.conf000066400000000000000000000001741515254461100272410ustar00rootroot00000000000000# Basic project name: foo min-version: 2.0 plugins: - origin: pip package-name: buildstream-plugins sources: - patch buildstream-plugins-2.7.0/tests/sources/patch/multiple-patches/target.bst000066400000000000000000000002311515254461100267160ustar00rootroot00000000000000kind: import description: This is the pony sources: - kind: local path: file.txt - kind: patch path: file_1.patch - kind: patch path: file_2.patch buildstream-plugins-2.7.0/tests/sources/patch/separate-patch-dir/000077500000000000000000000000001515254461100251175ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/patch/separate-patch-dir/file_1.patch000066400000000000000000000003001515254461100272700ustar00rootroot00000000000000diff --git a/file.txt b/file.txt index a496efe..341ef26 100644 --- a/file.txt +++ b/file.txt @@ -1 +1 @@ -This is a text file in a directory +This is text file in a directory with superpowers buildstream-plugins-2.7.0/tests/sources/patch/separate-patch-dir/files/000077500000000000000000000000001515254461100262215ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/patch/separate-patch-dir/files/test-dir/000077500000000000000000000000001515254461100277545ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/patch/separate-patch-dir/files/test-dir/file.txt000066400000000000000000000000431515254461100314310ustar00rootroot00000000000000This is a text file in a directory buildstream-plugins-2.7.0/tests/sources/patch/separate-patch-dir/project.conf000066400000000000000000000001741515254461100274360ustar00rootroot00000000000000# Basic project name: foo min-version: 2.0 plugins: - origin: pip package-name: buildstream-plugins sources: - patch buildstream-plugins-2.7.0/tests/sources/patch/separate-patch-dir/target.bst000066400000000000000000000002111515254461100271110ustar00rootroot00000000000000kind: import description: This is the pony sources: - kind: local path: files - kind: patch path: file_1.patch directory: test-dir buildstream-plugins-2.7.0/tests/sources/pip-build/000077500000000000000000000000001515254461100222305ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/pip-build/elements/000077500000000000000000000000001515254461100240445ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/pip-build/elements/base.bst000066400000000000000000000000551515254461100254700ustar00rootroot00000000000000kind: stack depends: - base/alpine-image.bst buildstream-plugins-2.7.0/tests/sources/pip-build/elements/base/000077500000000000000000000000001515254461100247565ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/pip-build/elements/base/alpine-image.bst000066400000000000000000000003241515254461100300170ustar00rootroot00000000000000kind: import description: Import an alpine image as the platform sources: - kind: tar url: alpine:integration-tests-base.v1.x86_64.tar.xz ref: 3eb559250ba82b64a68d86d0636a6b127aa5f6d25d3601a79f79214dc9703639 buildstream-plugins-2.7.0/tests/sources/pip-build/files/000077500000000000000000000000001515254461100233325ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/pip-build/files/pip-source/000077500000000000000000000000001515254461100254205ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/pip-build/files/pip-source/app1.py000066400000000000000000000001731515254461100266340ustar00rootroot00000000000000#!/usr/bin/env python3 from hellolib import hello def main(): hello("App1") if __name__ == "__main__": main() buildstream-plugins-2.7.0/tests/sources/pip-build/files/pip-source/myreqs.txt000066400000000000000000000000111515254461100274710ustar00rootroot00000000000000hellolib buildstream-plugins-2.7.0/tests/sources/pip-build/project.conf000066400000000000000000000004261515254461100245470ustar00rootroot00000000000000# test project config name: test min-version: 2.0 element-path: elements plugins: - origin: pip package-name: buildstream-plugins sources: - pip aliases: alpine: https://bst-integration-test-images.ams3.cdn.digitaloceanspaces.com/ project_dir: file://{project_dir} buildstream-plugins-2.7.0/tests/sources/pip.py000066400000000000000000000070101515254461100215030ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Pylint doesn't play well with fixtures and dependency injection from pytest # pylint: disable=redefined-outer-name import os import pytest from buildstream import _yaml from buildstream.exceptions import ErrorDomain from buildstream._testing import cli # pylint: disable=unused-import from buildstream_plugins.sources.pip import _match_package_name DATA_DIR = os.path.join( os.path.dirname(os.path.realpath(__file__)), "pip", ) def generate_project(project_dir): project_file = os.path.join(project_dir, "project.conf") _yaml.roundtrip_dump( { "name": "foo", "min-version": "2.0", "plugins": [ { "origin": "pip", "package-name": "buildstream-plugins", "sources": ["pip"], } ], }, project_file, ) # Test that without ref, consistency is set appropriately. @pytest.mark.datafiles(os.path.join(DATA_DIR, "no-ref")) def test_no_ref(cli, datafiles): project = str(datafiles) generate_project(project) assert cli.get_element_state(project, "target.bst") == "no reference" # Test that pip is not allowed to be the first source @pytest.mark.datafiles(os.path.join(DATA_DIR, "first-source-pip")) def test_first_source(cli, datafiles): project = str(datafiles) generate_project(project) result = cli.run(project=project, args=["show", "target.bst"]) result.assert_main_error(ErrorDomain.ELEMENT, None) # Test that error is raised when neither packges nor requirements files # have been specified @pytest.mark.datafiles(os.path.join(DATA_DIR, "no-packages")) def test_no_packages(cli, datafiles): project = str(datafiles) generate_project(project) result = cli.run(project=project, args=["show", "target.bst"]) result.assert_main_error(ErrorDomain.SOURCE, None) # Test that pip source parses tar ball names correctly for the ref @pytest.mark.parametrize( "tarball, expected_name, expected_version", [ ("dotted.package-0.9.8.tar.gz", "dotted.package", "0.9.8"), ("hyphenated-package-2.6.0.tar.gz", "hyphenated-package", "2.6.0"), ("underscore_pkg-3.1.0.tar.gz", "underscore_pkg", "3.1.0"), ("numbers2and5-1.0.1.tar.gz", "numbers2and5", "1.0.1"), ( "multiple.dots.package-5.6.7.tar.gz", "multiple.dots.package", "5.6.7", ), ( "multiple-hyphens-package-1.2.3.tar.gz", "multiple-hyphens-package", "1.2.3", ), ( "multiple_underscore_pkg-3.4.5.tar.gz", "multiple_underscore_pkg", "3.4.5", ), ("shortversion-1.0.tar.gz", "shortversion", "1.0"), ("longversion-1.2.3.4.tar.gz", "longversion", "1.2.3.4"), ], ) def test_match_package_name(tarball, expected_name, expected_version): name, version = _match_package_name(tarball) assert (expected_name, expected_version) == (name, version) buildstream-plugins-2.7.0/tests/sources/pip/000077500000000000000000000000001515254461100211335ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/pip/first-source-pip/000077500000000000000000000000001515254461100243465ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/pip/first-source-pip/target.bst000066400000000000000000000001671515254461100263520ustar00rootroot00000000000000kind: import description: pip should not be allowed to be the first source sources: - kind: pip packages: - flake8 buildstream-plugins-2.7.0/tests/sources/pip/no-packages/000077500000000000000000000000001515254461100233235ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/pip/no-packages/file000066400000000000000000000000151515254461100241610ustar00rootroot00000000000000Hello World! buildstream-plugins-2.7.0/tests/sources/pip/no-packages/target.bst000066400000000000000000000001621515254461100253220ustar00rootroot00000000000000kind: import description: The kind of this element is irrelevant. sources: - kind: local path: file - kind: pip buildstream-plugins-2.7.0/tests/sources/pip/no-ref/000077500000000000000000000000001515254461100223215ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/sources/pip/no-ref/file000066400000000000000000000000151515254461100231570ustar00rootroot00000000000000Hello World! buildstream-plugins-2.7.0/tests/sources/pip/no-ref/target.bst000066400000000000000000000002111515254461100243130ustar00rootroot00000000000000kind: import description: The kind of this element is irrelevant. sources: - kind: local path: file - kind: pip packages: - flake8 buildstream-plugins-2.7.0/tests/sources/pip_build.py000066400000000000000000000216171515254461100226730ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Pylint doesn't play well with fixtures and dependency injection from pytest # pylint: disable=redefined-outer-name import os import pytest from buildstream import _yaml from buildstream._testing import cli_integration as cli # pylint: disable=unused-import from buildstream._testing.integration import assert_contains from buildstream._testing.integration import integration_cache # pylint: disable=unused-import from buildstream._testing._utils.site import HAVE_SANDBOX from tests.testutils.python_repo import setup_pypi_repo # pylint: disable=unused-import pytestmark = pytest.mark.integration DATA_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "pip-build") @pytest.mark.datafiles(DATA_DIR) def test_pip_source_import_packages(cli, datafiles, setup_pypi_repo): project = str(datafiles) checkout = os.path.join(cli.directory, "checkout") element_path = os.path.join(project, "elements") element_name = "pip/hello.bst" # check that exotically named packages are imported correctly myreqs_packages = "hellolib" dependencies = [ "app2", "app.3", "app-4", "app_5", "app.no.6", "app-no-7", "app_no_8", ] mock_packages = {myreqs_packages: {package: {} for package in dependencies}} # create mock pypi repository pypi_repo = os.path.join(project, "files", "pypi-repo") os.makedirs(pypi_repo, exist_ok=True) setup_pypi_repo(mock_packages, pypi_repo) element = { "kind": "import", "sources": [ {"kind": "local", "path": "files/pip-source"}, { "kind": "pip", "url": "file://{}".format(os.path.realpath(pypi_repo)), "packages": [myreqs_packages], }, ], } os.makedirs( os.path.dirname(os.path.join(element_path, element_name)), exist_ok=True, ) _yaml.roundtrip_dump(element, os.path.join(element_path, element_name)) result = cli.run(project=project, args=["source", "track", element_name]) assert result.exit_code == 0 result = cli.run(project=project, args=["build", element_name]) assert result.exit_code == 0 result = cli.run( project=project, args=["artifact", "checkout", element_name, "--directory", checkout], ) assert result.exit_code == 0 assert_contains( checkout, [ "/.bst_pip_downloads", "/.bst_pip_downloads/hellolib-0.1.tar.gz", "/.bst_pip_downloads/app2-0.1.tar.gz", "/.bst_pip_downloads/app_3-0.1.tar.gz", "/.bst_pip_downloads/app_4-0.1.tar.gz", "/.bst_pip_downloads/app_5-0.1.tar.gz", "/.bst_pip_downloads/app_no_6-0.1.tar.gz", "/.bst_pip_downloads/app_no_7-0.1.tar.gz", "/.bst_pip_downloads/app_no_8-0.1.tar.gz", ], ) @pytest.mark.datafiles(DATA_DIR) def test_pip_source_import_requirements_files(cli, datafiles, setup_pypi_repo): project = str(datafiles) checkout = os.path.join(cli.directory, "checkout") element_path = os.path.join(project, "elements") element_name = "pip/hello.bst" # check that exotically named packages are imported correctly myreqs_packages = "hellolib" dependencies = [ "app2", "app.3", "app-4", "app_5", "app.no.6", "app-no-7", "app_no_8", ] mock_packages = {myreqs_packages: {package: {} for package in dependencies}} # create mock pypi repository pypi_repo = os.path.join(project, "files", "pypi-repo") os.makedirs(pypi_repo, exist_ok=True) setup_pypi_repo(mock_packages, pypi_repo) element = { "kind": "import", "sources": [ {"kind": "local", "path": "files/pip-source"}, { "kind": "pip", "url": "file://{}".format(os.path.realpath(pypi_repo)), "requirements-files": ["myreqs.txt"], }, ], } os.makedirs( os.path.dirname(os.path.join(element_path, element_name)), exist_ok=True, ) _yaml.roundtrip_dump(element, os.path.join(element_path, element_name)) result = cli.run(project=project, args=["source", "track", element_name]) assert result.exit_code == 0 result = cli.run(project=project, args=["build", element_name]) assert result.exit_code == 0 result = cli.run( project=project, args=["artifact", "checkout", element_name, "--directory", checkout], ) assert result.exit_code == 0 assert_contains( checkout, [ "/.bst_pip_downloads", "/.bst_pip_downloads/hellolib-0.1.tar.gz", "/.bst_pip_downloads/app2-0.1.tar.gz", "/.bst_pip_downloads/app_3-0.1.tar.gz", "/.bst_pip_downloads/app_4-0.1.tar.gz", "/.bst_pip_downloads/app_5-0.1.tar.gz", "/.bst_pip_downloads/app_no_6-0.1.tar.gz", "/.bst_pip_downloads/app_no_7-0.1.tar.gz", "/.bst_pip_downloads/app_no_8-0.1.tar.gz", ], ) @pytest.mark.datafiles(DATA_DIR) @pytest.mark.skipif(not HAVE_SANDBOX, reason="Only available with a functioning sandbox") def test_pip_source_build(cli, datafiles, setup_pypi_repo): project = str(datafiles) element_path = os.path.join(project, "elements") element_name = "pip/hello.bst" # check that exotically named packages are imported correctly myreqs_packages = "hellolib" dependencies = [ "app2", "app.3", "app-4", "app_5", "app.no.6", "app-no-7", "app_no_8", ] mock_packages = {myreqs_packages: {package: {} for package in dependencies}} # create mock pypi repository pypi_repo = os.path.join(project, "files", "pypi-repo") os.makedirs(pypi_repo, exist_ok=True) setup_pypi_repo(mock_packages, pypi_repo) realpath_repo = os.path.realpath(pypi_repo) element = { "kind": "manual", "depends": ["base.bst"], "sources": [ {"kind": "local", "path": "files/pip-source"}, { "kind": "pip", "url": "file://{}".format(realpath_repo), "requirements-files": ["myreqs.txt"], "packages": dependencies, }, ], "config": { "install-commands": [ "pip3 install --no-index --prefix %{install-root}/usr .bst_pip_downloads/*.tar.gz", "install app1.py %{install-root}/usr/bin/", ] }, } os.makedirs( os.path.dirname(os.path.join(element_path, element_name)), exist_ok=True, ) _yaml.roundtrip_dump(element, os.path.join(element_path, element_name)) result = cli.run(project=project, args=["source", "track", element_name]) assert result.exit_code == 0 # # Lets sneak in here and test out that Source.collect_source_info() works as expected # # The ref for this generated pip source is: # # "app2==0.1\napp_3==0.1\napp_4==0.1\napp_5==0.1\napp_no_6==0.1\napp_no_7==0.1\napp_no_8==0.1\nhellolib==0.1" # # Lets just make an assertion on the first dependency app2, which is the second in the list after the local source for this element # result = cli.run( project=project, silent=True, args=["show", "--deps", "none", "--format", "element:\n%{source-info}", element_name], ) result.assert_success() loaded = _yaml.load_data(result.output) sources = loaded.get_sequence("element") source_info = sources.mapping_at(1) assert source_info.get_str("kind") == "pip" assert source_info.get_str("url") == f"file://{realpath_repo}" assert source_info.get_str("medium") == "pypi" assert source_info.get_str("version-type") == "indexed-version" assert source_info.get_str("version") == "0.1" assert source_info.get_str("version-guess") == "0.1" extra_data = source_info.get_mapping("extra-data", None) assert extra_data is not None assert extra_data.get_str("package-name", "app2") # Go ahead and build result = cli.run(project=project, args=["build", element_name]) assert result.exit_code == 0 # Use a build shell to assert the output of something we installed result = cli.run(project=project, args=["shell", element_name, "/usr/bin/app1.py"]) assert result.exit_code == 0 assert result.output == "Hello App1! This is hellolib\n" buildstream-plugins-2.7.0/tests/testutils/000077500000000000000000000000001515254461100207205ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/testutils/__init__.py000066400000000000000000000000001515254461100230170ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/testutils/python_repo.py000066400000000000000000000076611515254461100236520ustar00rootroot00000000000000import os import re import shutil import subprocess import sys import pytest SETUP_TEMPLATE = """\ from setuptools import setup setup( name='{name}', version='{version}', description='{name}', packages=['{pkgdirname}'], install_requires={pkgdeps}, entry_points={{ 'console_scripts': [ '{pkgdirname}={pkgdirname}:main' ] }} ) """ # All packages generated via generate_pip_package will have the functions below INIT_TEMPLATE = """\ def main(): print('This is {name}') def hello(actor='world'): print('Hello {{}}! This is {name}'.format(actor)) """ HTML_TEMPLATE = """\ Links for {name} {links} """ HTML_LINK_TEMPLATE = """\ {dist}
""" # Creates a simple python source distribution and copies this into a specified # directory which is to serve as a mock python repository # # Args: # tmpdir (str): Directory in which the source files will be created # pypi (str): Directory serving as a mock python repository # name (str): The name of the package to be created # version (str): The version of the package to be created # # Returns: # None # def generate_pip_package(tmpdir, pypi, name, version="0.1", dependencies=None): if dependencies is None: dependencies = [] # check if package already exists in pypi pypi_package = os.path.join(pypi, re.sub("[^0-9a-zA-Z]+", "-", name)) if os.path.exists(pypi_package): return # create the package source files in tmpdir resulting in a directory # tree resembling the following structure: # # tmpdir # |-- setup.py # `-- package # `-- __init__.py # setup_file = os.path.join(tmpdir, "setup.py") pkgdirname = re.sub("[^0-9a-zA-Z]+", "", name) with open(setup_file, "w", encoding="utf-8") as f: f.write( SETUP_TEMPLATE.format( name=name, version=version, pkgdirname=pkgdirname, pkgdeps=dependencies, ) ) os.chmod(setup_file, 0o755) package = os.path.join(tmpdir, pkgdirname) os.makedirs(package) main_file = os.path.join(package, "__init__.py") with open(main_file, "w", encoding="utf-8") as f: f.write(INIT_TEMPLATE.format(name=name)) os.chmod(main_file, 0o644) # Build source distribution subprocess.run([sys.executable, "-m", "build", "--sdist"], cwd=tmpdir, check=True) # create directory for this package in pypi resulting in a directory # tree resembling the following structure: # # pypi # `-- pypi_package # |-- index.html # `-- foo-0.1.tar.gz # os.makedirs(pypi_package) links = "" # copy generated tarfile to pypi package dist_dir = os.path.join(tmpdir, "dist") for tar in os.listdir(dist_dir): tarpath = os.path.join(dist_dir, tar) shutil.copy(tarpath, pypi_package) links += HTML_LINK_TEMPLATE.format(dist=tar) # add an index html page index_html = os.path.join(pypi_package, "index.html") with open(index_html, "w", encoding="utf-8") as f: f.write(HTML_TEMPLATE.format(name=name, links=links)) @pytest.fixture def setup_pypi_repo(tmpdir): def create_pkgdir(package): pkgdirname = re.sub("[^0-9a-zA-Z]+", "", package) pkgdir = os.path.join(str(tmpdir), pkgdirname) os.makedirs(pkgdir) return pkgdir def add_packages(packages, pypi_repo): for package, dependencies in packages.items(): pkgdir = create_pkgdir(package) generate_pip_package( pkgdir, pypi_repo, package, dependencies=list(dependencies.keys()), ) for dependency, dependency_dependencies in dependencies.items(): add_packages({dependency: dependency_dependencies}, pypi_repo) return add_packages buildstream-plugins-2.7.0/tests/testutils/repo/000077500000000000000000000000001515254461100216655ustar00rootroot00000000000000buildstream-plugins-2.7.0/tests/testutils/repo/__init__.py000066400000000000000000000000621515254461100237740ustar00rootroot00000000000000from .bzrrepo import Bzr from .gitrepo import Git buildstream-plugins-2.7.0/tests/testutils/repo/bzrrepo.py000066400000000000000000000034301515254461100237220ustar00rootroot00000000000000import os import subprocess import pytest from buildstream._testing import Repo from ..site import BZR, BZR_ENV, HAVE_BZR class Bzr(Repo): def __init__(self, directory, subdir="repo"): if not HAVE_BZR: pytest.skip("bzr is not available") super().__init__(directory, subdir) self.bzr = BZR self.env = os.environ.copy() self.env.update(BZR_ENV) def create(self, directory): # Work around race condition in bzr's creation of ~/.bazaar in # ensure_config_dir_exists() when running tests in parallel. bazaar_config_dir = os.path.expanduser("~/.bazaar") os.makedirs(bazaar_config_dir, exist_ok=True) branch_dir = os.path.join(self.repo, "trunk") subprocess.call([self.bzr, "init-repo", self.repo], env=self.env) subprocess.call([self.bzr, "init", branch_dir], env=self.env) self.copy_directory(directory, branch_dir) subprocess.call([self.bzr, "add", "."], env=self.env, cwd=branch_dir) subprocess.call( [self.bzr, "commit", '--message="Initial commit"'], env=self.env, cwd=branch_dir, ) return self.latest_commit() def source_config(self, ref=None): config = { "kind": "bzr", "url": "file://" + self.repo, "track": "trunk", } if ref is not None: config["ref"] = ref return config def latest_commit(self): return subprocess.check_output( [ self.bzr, "version-info", "--custom", "--template={revno}", os.path.join(self.repo, "trunk"), ], env=self.env, universal_newlines=True, ).strip() buildstream-plugins-2.7.0/tests/testutils/repo/gitrepo.py000066400000000000000000000073631515254461100237210ustar00rootroot00000000000000import os import shutil import subprocess import pytest from buildstream._testing import Repo from ..site import GIT, GIT_ENV, HAVE_GIT class Git(Repo): def __init__(self, directory, subdir="repo"): if not HAVE_GIT: pytest.skip("git is not available") self.submodules = {} super().__init__(directory, subdir) self.env = os.environ.copy() self.env.update(GIT_ENV) def _run_git(self, *args, **kwargs): argv = [GIT] argv.extend(args) if "env" not in kwargs: kwargs["env"] = dict(self.env, PWD=self.repo) kwargs.setdefault("cwd", self.repo) kwargs.setdefault("check", True) return subprocess.run(argv, **kwargs) # pylint: disable=subprocess-run-check def create(self, directory): self.copy_directory(directory, self.repo) self._run_git("init", ".") self._run_git("add", ".") self._run_git("commit", "-m", "Initial commit") return self.latest_commit() def add_tag(self, tag): self._run_git("tag", tag) def add_annotated_tag(self, tag, message): self._run_git("tag", "-a", tag, "-m", message) def add_commit(self): self._run_git("commit", "--allow-empty", "-m", "Additional commit") return self.latest_commit() def add_file(self, filename): shutil.copy(filename, self.repo) self._run_git("add", os.path.basename(filename)) self._run_git("commit", "-m", "Added {}".format(os.path.basename(filename))) return self.latest_commit() def modify_file(self, new_file, path): shutil.copy(new_file, os.path.join(self.repo, path)) self._run_git("commit", path, "-m", "Modified {}".format(os.path.basename(path))) return self.latest_commit() def add_submodule(self, subdir, url=None, checkout=None): submodule = {} if checkout is not None: submodule["checkout"] = checkout if url is not None: submodule["url"] = url self.submodules[subdir] = submodule self._run_git("-c", "protocol.file.allow=always", "submodule", "add", url, subdir) self._run_git("commit", "-m", "Added the submodule") return self.latest_commit() # This can also be used to a file or a submodule def remove_path(self, path): self._run_git("rm", path) self._run_git("commit", "-m", "Removing {}".format(path)) return self.latest_commit() def source_config(self, ref=None): return self.source_config_extra(ref) def source_config_extra(self, ref=None, checkout_submodules=None): config = { "kind": "git", "url": "file://" + self.repo, "track": "master", } if ref is not None: config["ref"] = ref if checkout_submodules is not None: config["checkout-submodules"] = checkout_submodules if self.submodules: config["submodules"] = dict(self.submodules) return config def latest_commit(self): return self._run_git( "rev-parse", "HEAD", stdout=subprocess.PIPE, universal_newlines=True, ).stdout.strip() def branch(self, branch_name): self._run_git("checkout", "-b", branch_name) def delete_tag(self, tag_name): self._run_git("tag", "-d", tag_name) def checkout(self, commit): self._run_git("checkout", commit) def merge(self, commit): self._run_git("merge", "-m", "Merge", commit) return self.latest_commit() def rev_parse(self, rev): return self._run_git( "rev-parse", rev, stdout=subprocess.PIPE, universal_newlines=True, ).stdout.strip() buildstream-plugins-2.7.0/tests/testutils/site.py000066400000000000000000000023751515254461100222450ustar00rootroot00000000000000import subprocess from typing import Optional from buildstream import utils, ProgramNotFoundError GIT: Optional[str] BZR: Optional[str] try: GIT = utils.get_host_tool("git") HAVE_GIT = True out = str(subprocess.check_output(["git", "--version"]), "utf-8") # e.g. on Git for Windows we get "git version 2.21.0.windows.1". # e.g. on Mac via Homebrew we get "git version 2.19.0". version = tuple(int(x) for x in out.split(" ")[2].split(".")[:3]) HAVE_OLD_GIT = version < (1, 8, 5) GIT_ENV = { "GIT_AUTHOR_DATE": "1320966000 +0200", "GIT_AUTHOR_NAME": "tomjon", "GIT_AUTHOR_EMAIL": "tom@jon.com", "GIT_COMMITTER_DATE": "1320966000 +0200", "GIT_COMMITTER_NAME": "tomjon", "GIT_COMMITTER_EMAIL": "tom@jon.com", } except ProgramNotFoundError: GIT = None HAVE_GIT = False HAVE_OLD_GIT = False GIT_ENV = {} try: BZR = utils.get_host_tool("bzr") HAVE_BZR = True # Breezy 3.0 supports `BRZ_EMAIL` but not `BZR_EMAIL` BZR_ENV = { "BZR_EMAIL": "Testy McTesterson ", "BRZ_EMAIL": "Testy McTesterson ", } except ProgramNotFoundError: BZR = None HAVE_BZR = False BZR_ENV = {} buildstream-plugins-2.7.0/tox.ini000066400000000000000000000065271515254461100170430ustar00rootroot00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # Tox global configuration # [tox] envlist = py{310,311,312,313,314}-{bst-fixed,bst-master} skip_missing_interpreters = true # # Defaults for all environments # # Anything specified here is inherited by the sections # [testenv] commands = bst --version {toxinidir}/tests/bzr_wrapper {envdir}/bin pytest --basetemp {envtmpdir} {posargs} deps = -rrequirements/test-requirements.txt -rrequirements/plugin-requirements.txt git+https://github.com/apache/buildstream@{env:BST_VERSION} passenv = ARTIFACT_CACHE_SERVICE BST_FORCE_BACKEND BST_FORCE_SANDBOX GI_TYPELIB_PATH INTEGRATION_CACHE http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY PYTEST_* REMOTE_EXECUTION_SERVICE SOURCE_CACHE_SERVICE SSL_CERT_FILE # # These keys are not inherited by any other sections # setenv = py{310,311,312,313,314}: XDG_CACHE_HOME = {envtmpdir}/cache py{310,311,312,313,314}: XDG_CONFIG_HOME = {envtmpdir}/config py{310,311,312,313,314}: XDG_DATA_HOME = {envtmpdir}/share !master: BST_VERSION = 2.7.0 master: BST_VERSION = master allowlist_externals = py{310,311,312,313,314}: mv mkdir {toxinidir}/tests/bzr_wrapper # # Code formatters # [testenv:format] skip_install = True deps = black==22.3.0 commands = black {posargs: src tests setup.py} # # Code format checkers # [testenv:format-check] skip_install = True deps = black==22.3.0 commands = black --check --diff {posargs: src tests setup.py} # # Running linters # [testenv:lint] commands_pre = # Build C extensions to allow Pylint to analyse them {envpython} setup.py build_ext --inplace commands = pylint {posargs: buildstream_plugins tests setup.py} # # Running static type checkers # [testenv:mypy] skip_install = True commands = mypy {posargs} deps = mypy==1.13.0 types-protobuf types-python-dateutil types-setuptools types-ujson -rrequirements/plugin-requirements.txt -rrequirements/test-requirements.txt -rrequirements/mypy-requirements.txt git+https://github.com/apache/buildstream@{env:BST_VERSION} # # Building documentation # [testenv:docs] commands = make -C doc # sphinx_rtd_theme < 0.4.2 breaks search functionality for Sphinx >= 1.8 deps = sphinx >= 1.8.5 sphinx_rtd_theme >= 0.4.2 -rrequirements/plugin-requirements.txt git+https://github.com/apache/buildstream@{env:BST_VERSION} passenv = BST_FORCE_SESSION_REBUILD BST_SOURCE_CACHE HOME LANG LC_ALL allowlist_externals = make # # Publish a release, make sure to update the version in setup.py first # [testenv:release] skip_install = true commands = python3 setup.py sdist bdist_wheel twine upload -r pypi dist/* deps = twine wheel passenv = TWINE_USERNAME TWINE_PASSWORD