subtree updates

poky: 8d0ba08aa6..2696bf8cf3:
  Adam Johnston (1):
        useradd_base: Fix sed command line for passwd-expire

  Adrian Freihofer (1):
        vscode: add minimal configuration

  Alassane Yattara (44):
        bitbake: Update toaster-requirements to add django-log-viewer==1.1.7
        bitbake: toaster: bug-fix on tests.browser.test_most_recent_builds_states
        bitbake: Toaster: Bug-fix failure on tests.browser.test_layerdetails_page
        bitbake: Toaster: Fixed javascript issue on tests.browser.test_js_unit_tests
        bitbake: Toaster: bug-fix on /toastermain/logs.py
        bitbake: Toaster: bug-fix on custom image test cases
        bitbake: Toaster: bug-fix on tests/views/test_views.py
        bitbake: Toaster: bug-fix on tests.views.test_views.py
        bitbake: toaster: Write logs to BUILDDIR/toaster_logs
        bitbake: toaster: Add toaster-tests-requirements.txt to add pytest and some plugins
        bitbake: toaster: Update orm.models to catch error ProcessLookupError
        bitbake: toaster: Bug-fix pytest and Failed: Database access not allowed
        bitbake: toaster: fixed pytest error: Database access not allowed, use the "django_db"
        bitbake: toaster: Bug-fix django.db.utils.IntegrityError: Problem installing fixture
        bitbake: toaster: fixed: Tests fail when executed one after the other out of sequence
        bitbake: toaster: Added pytest.ini file
        bitbake: toaster: Check info_sign is visible and clickable in landing page
        bitbake: toaster: Test documentation link in landing header is displayed
        bitbake: toaster: Test jumbotron links visible and clickable
        bitbake: toaster: Bug-fix webdriver No parameter named options
        bitbake: Toaster: Write UI TestCase create new project
        bitbake: Toaster: Test create new project without project name
        bitbake: Toaster: Write UI TestCase import new project using
        bitbake: toaster/tests: Add UI TestCase to test if 'no build' message is shown
        bitbake: toaster/tests: Add UI TestCase to test search box on all build page
        bitbake: toaster/tests: Add UI TestCase to test the filtering feature on 'failure tasks' column
        bitbake: toaster/tests: Add UI TestCase to test filtering feature on 'completed_on' column
        bitbake: toaster/tests: Add UI TestCase to test "edit column" feature show/hide column
        bitbake: toaster/tests: Add UI TestCase to test "show rows" feature, change displaying rows in table
        bitbake: toaster/tests: Add UI TestCase for deleting project
        bitbake: toaster/tests: Add UI TestCase for Visualize all projects
        bitbake: toaster/tests: Add UI TestCase for visualize all projects edit column
        bitbake: toaster/tests: Add UI TestCase for visualize all projects show rows
        bitbake: toaster/tests/create_new_project: Code cleanup
        bitbake: toaster/tests: Add UI TestCase - Check project header contains right items
        bitbake: toaster/tests: Add UI TestCase - Test edit project name on project page
        bitbake: toaster/tests: Add UI TestCase - Test project page has right tabs displayed
        bitbake: toaster/tests: Add UI TestCase - Test project config tab navigation:
        bitbake: toaster/tests: Add UI TestCase - Test project config tab
        bitbake: toaster/tests: Add UI TestCase - Test project page tab import layer
        bitbake: toaster/tests: Add UI TestCase - Test project page tab "New custom image"
        bitbake: toaster/tests: Add UI TestCase - Test project page section images
        bitbake: toaster/tests: Add UI TestCase for the edit column feature in image recipe
        bitbake: toaster/tests: Add UI TestCase - Test the show rows feature in image recipe

  Alberto Pianon (1):
        bitbake: fetch2: Add API for upstream source tracing

  Alejandro Hernandez Samaniego (2):
        qemuarmv5: Drop QB_DTB conditional for older kernels
        baremetal-helloworld: Pull in fix for race condition on x86-64

  Alex Stewart (1):
        libsndfile1: fix CVE-2022-33065

  Alexander Kanavin (10):
        scripts/bitbake-whatchanged: remove
        selftest/buildoptions: tag the download mirror test with 'yocto-mirrors'
        bitbake: runqueue.py: clarify that 'closest' signature means 'most recent' (and not closest in its content)
        selftest/sstatetests: add tests for 'bitbake -S printdiff'
        lib/oe/sstatesig.py: dump locked.sigs.inc only when explicitly asked via -S lockedsigs
        selftest/sstatetests: add a test for CDN sstate cache
        populate_sdk_ext.bbclass: do not symlink unfsd from sdk image sysroot into eSDK tools path
        meta/lib/oe/copy_buildsystem.py: do not derefence symlinks
        scripts/esdk-tools: use a dedicated, static directory for esdk tools
        populate_sdk_ext: split copy_buildsystem() into logical steps defined as functions

  Alexander Lussier-Cullen (2):
        bitbake: toaster/tests: add passthroughs for relevant build environment variables
        bitbake: toaster: make django temp directory configurable

  Alexandre Belloni (1):
        strace: further clean up of ptest folders

  Alexis Lothoré (5):
        scripts/resulttool: limit the number of changes displayed per test
        scripts/resulttool: rearrange regressions report order
        scripts/resulttool: make additional info more compact
        scripts/yocto_testresults_query: add option to change display limit
        scripts/resulttool: group all regressions in regression report

  Anuj Mittal (9):
        gstreamer1.0: upgrade 1.22.6 -> 1.22.7
        gsettings-desktop-schemas: upgrade 44.0 -> 45.0
        harfbuzz: upgrade 8.2.2 -> 8.3.0
        libnotify: upgrade 0.8.2 -> 0.8.3
        libtirpc: upgrade 1.3.3 -> 1.3.4
        mmc-utils: upgrade to latest revision
        puzzles: upgrade to latest revision
        sqlite3: upgrade 3.43.2 -> 3.44.0
        vulkan: upgrade 1.3.261.1 -> 1.3.268.0

  Archana Polampalli (1):
        vim: Upgrade 9.0.2048 -> 9.0.2068

  Arne Schwerdt (1):
        ref-manual: Warn about COMPATIBLE_MACHINE skipping native recipes

  BELHADJ SALEM Talel (8):
        bitbake.conf: Drop DEPLOY_DIR_TAR
        ref-manual: Fix PACKAGECONFIG term and add an example
        dev-manual: layers: Add notes about layer.conf
        ref-manual: variables: add RECIPE_SYSROOT and RECIPE_SYSROOT_NATIVE
        ref-manual: variables: add TOOLCHAIN_OPTIONS variable
        ref-manual: variables: add example for SYSROOT_DIRS variable
        bitbake: Fix find_bbfiles string endswith call
        overview-manual: concepts: Add Bitbake Tasks Map

  Bastian Krause (1):
        linux-firmware: add new fw file to ${PN}-rtl8821

  Bruce Ashfield (22):
        linux-yocto/6.1: update to v6.1.56
        linux-yocto/6.5: update to v6.5.6
        linux-yocto/6.1: tiny: fix arm 32 boot
        linux-yocto/6.5: tiny: fix arm 32 boot
        linux-yocto/6.5: update to v6.5.7
        linux-yocto/6.1: update to v6.1.57
        linux-yocto/6.4: drop recipes
        linux-yocto/6.5: avoid serial port suspend issues
        linux-yocto/6.5: config: remove VIDEO_STK1160_COMMON
        linux-yocto/6.5: serial: core: integrate upstream fixes
        linux-yocto/6.5: update to v6.5.8
        linux-yocto/6.1: update to v6.1.59
        linux-yocto/6.5: update to v6.5.9
        linux-yocto/6.1: update to v6.1.60
        kern-tools: make lower context patches reproducible
        kern-tools: bump SRCREV for queue processing changes
        kern-tools: update SRCREV to include SECURITY.md file
        kernel-yocto: improve metadata patching
        linux-yocto/6.1: cfg: restore CONFIG_DEVMEM
        linux-yocto/6.1: update to v6.1.61
        linux-yocto/6.5: cfg: restore CONFIG_DEVMEM
        linux-yocto/6.5: update to v6.5.10

  Chen Qi (2):
        kernel.bbclass: add preceding space in appendVar setting
        systemd: fix DynamicUser issue

  Chris Laplante (4):
        bitbake: codeparser: replace deprecated ast.Str and 's'
        bitbake: runqueue: set has 'add', not 'append' method
        bitbake: codeparser: add missing 'import os'
        bitbake: codegen: cleanup deprecated AST usages

  Deepthi Hemraj (1):
        binutils: Fix CVE-2022-47007

  Desone Burns (1):
        bitbake: bitbake: fetch2: git: Update Git-LFS download and tests

  Dmitry Baryshkov (11):
        kernel-arch: drop CCACHE from KERNEL_STRIP definition
        meson: use correct targets for rust binaries
        linux-firmware: upgrade 20230804 -> 20231030
        linux-firmware: add missing depenencies on license packages
        linux-firmware: add notice file to sdm845 modem firmware
        linux-firmware: add audio topology symlink to the X13's audio package
        linux-firmware: package firmware for Qualcomm Adreno a702
        linux-firmware: package firmware for Qualcomm QCM2290 / QRB4210
        linux-firmware: package Qualcomm Venus 6.0 firmware
        linux-firmware: package Robotics RB5 sensors DSP firmware
        libdrm: upgrade 2.4.116 -> 2.4.117

  Eero Aaltonen (3):
        base-files, systemd: add nss-resolve plugin
        systemd: add option to use stub-resolv.conf
        ref-manual: add systemd-resolved to distro features

  Etienne Cordonnier (1):
        bitbake: bitbake-worker: add header with length of message

  Fabio Estevam (1):
        packagegroup-core-tools-profile: Remove PROFILE_TOOLS_X

  Fahad Arslan (1):
        linux-firmware: create separate packages

  Felix Moessbauer (1):
        bitbake: fetch2/aws: forward env-vars used in gitlab-ci K8s

  Florian Wickert (1):
        systemd: fix libnss-mymachines packaging

  Glenn Strauss (3):
        lighttpd: upgrade 1.4.71 -> 1.4.72
        lighttpd: update init script
        lighttpd: modernize lighttpd.conf

  Javier Tia (1):
        kernel-arch: use ccache only for compiler

  Jermain Horsman (3):
        lib/oe/buildcfg.py: Include missing import
        lib/oe/buildcfg.py: Remove unused parameter
        lib/bblayers/setupwriters/oe-setup-layers.py: Fix indentation

  Joakim Tjernlund (1):
        sed -i destroys symlinks

  Johannes Schneider (1):
        base-files: profile: allow profile.d to set EDITOR

  Jon Mason (2):
        qemu: drop unreferenced patch
        linux-yocto: Update dtb path for qemuarmv5

  Jose Quaresma (5):
        sstatesig: be more precise and show the full path in exceptions
        systemd: sort packages before pn
        systemd: add systemd-crypt package
        systemd: add cryptsetup-plugins package config
        systemd: add p11kit package config

  Joshua Watt (24):
        goarch: Move Go architecture mapping to a library
        bitbake: asyncrpc: Abstract sockets
        bitbake: hashserv: Add websocket connection implementation
        bitbake: asyncrpc: Add context manager API
        bitbake: hashserv: tests: Add external database tests
        bitbake: asyncrpc: Prefix log messages with client info
        bitbake: bitbake-hashserv: Allow arguments from environment
        bitbake: hashserv: Abstract database
        bitbake: hashserv: Add SQLalchemy backend
        bitbake: hashserv: Implement read-only version of "report" RPC
        bitbake: asyncrpc: Add InvokeError
        bitbake: asyncrpc: client: Prevent double closing of loop
        bitbake: asyncrpc: client: Add disconnect API
        bitbake: hashserv: Add user permissions
        bitbake: hashserv: Add become-user API
        bitbake: hashserv: Add db-usage API
        bitbake: hashserv: Add database column query API
        bitbake: hashserv: test: Add bitbake-hashclient tests
        bitbake: bitbake-hashclient: Output stats in JSON format
        bitbake: bitbake-hashserver: Allow anonymous permissions to be space separated
        bitbake: hashserv: tests: Allow authentication for external server tests
        bitbake: hashserv: Allow self-service deletion
        bitbake: hashserv: server: Add owner if user is logged in
        bitbake: asyncrpc: Add option to set log level when running as a process

  Julien Stephan (10):
        oeqa/selftest/devtool: abort if a local workspace already exist
        oeqa/selftest/devtool: remove spaces on empty line
        recipetool/create_buildsys_python: fix license note
        recipetool/create_buildsys_python: prefix created recipes with python3-
        recipetool/create_buildsys_python: refactor code for futur PEP517 addition
        recipetool/create_buildsys_python: add PEP517 support
        oeqa/selftest/recipetool: add selftest for PEP-517 recipe creation
        oeqa/selftest/devtool: fix test_devtool_modify_overrides test
        bitbake: bitbake: utils: remove spaces on empty lines
        bitbake: fetch2: git: add missing destsuffix and subpath parameters in docstrings

  Jérémy Rosen (5):
        insane: Add unimplemented-ptest infrastructure
        insane: Detect python and perl based tests
        insane: Detect build-system test harnesses
        insane: Add a naive heuristic to detect test subdirectories
        ref-manual: Add documentation for the unimplemented-ptest QA warning

  Jörg Sommer (3):
        libtirpc: Support ipv6 in DISTRO_FEATURES
        base-files: Remove localhost ::1 from hosts if ipv6 missing
        package_qa_check_rdepends: Allow /usr/bin/sh if usrmerge

  Khem Raj (23):
        gcompat: Add fcntl64 wrapper
        gcompat: Upgrade to 1.1.0 release
        python3-urllib3: Update to 2.0.6
        llvm: Upgrade to 17.0.3
        shared-mime-info: Fix missing sentinel warning
        openssl: Match target name for riscv64/riscv32
        openssl: Inherit riscv32 config from latomic config on linux
        kernel.bbclass: Use strip utility used for kernel build in do_package
        python3-urllib3: Upgrade to 2.0.7
        qemuriscv: Add to common MACHINE_FEATURES instead of overriding them
        meson: Add check for riscv64 in link template
        machine-sdk: Add SDK_ARCH for riscv64
        uninative.bbclass: Add ldso information for riscv64
        rust-cross-canadian: Add riscv64 to cross-canadian hosts
        cdrtools: Fix build on riscv64
        llvm: Upgrade to 17.0.4 release
        systemd: Make libnss-mymachines conditional upon packageconfig
        ptest-packagelists: Remove strace/valgrind/lttng-tools on riscv32
        libarchive: Add packageconfig knob for libb2
        librsvg: Fix build for riscv32
        librsvg: Enable 64bit atomics in crossbeam again for riscv32
        libsoup: Upgrade to 3.4.2 -> 3.4.4
        llvm: Upgrade to 17.0.5

  Lee Chee Yang (6):
        qemu: ignore RHEL specific CVE-2023-2680
        machine: drop obsolete SERIAL_CONSOLES_CHECK
        documentation.conf: drop SERIAL_CONSOLES_CHECK
        release-notes-4.3: add Repositories / Downloads section
        migration-guide: add release notes for 4.0.14
        migration-guide: add release notes for 4.2.4

  Logan Gunthorpe (1):
        runqemu: Add squashfs filesystem types

  Lukas Funke (5):
        classes: go-vendor: Add go-vendor class
        selftest: recipetool: Add test for go recipe handler
        recipetool: Ignore *.go files while scanning for licenses
        recipetool: Add handler to create go recipes
        udev-extraconf: mount.sh: check if filesystem is supported before mounting

  Malte Schmidt (3):
        systemd: use nonarch libdir for tmpfiles.d
        pam: use nonarch libdir for tmpfiles.d
        sysstat: use nonarch libdir for tmpfiles.d

  Marcus Folkesson (1):
        qemuboot.bbclass: fix typos in documentation

  Markus Fuchs (1):
        systemd: Add 'no-ntp-fallback' PACKAGECONFIG option

  Markus Volk (6):
        libcroco: drop recipe
        gnomebase.bbclass: Use meson as default buildsystem
        ghostscript: Build and install shared lib
        cups: Upgrade 2.4.6 -> 2.4.7
        gtk: Add rdepend on printbackend for cups
        ffmpeg: Upgrade 6.0 -> 6.1

  Marlon Rodriguez Garcia (6):
        bitbake: toaster: updated bootstrap version 3.3.6 -> 3.3.7
        bitbake: toaster: Update bootstrap version to 3.4.1
        bitbake: toaster: update jquery version 2.0.3 -> 3.7.1
        bitbake: toaster: fixed functional test
        bitbake: toaster: add tox.ini file to execute test suite
        bitbake: toaster: replace deprecated tags ifequal and ifnotequal

  Marta Rybczynska (6):
        SECURITY.md: add file
        bitbake: SECURITY.md: add file
        dev-manual: add security team processes
        python3-beartype: upgrade 0.16.2 -> 0.16.4
        python3-spdx-tools: upgrade 0.8.1 -> 0.8.2
        dev-manual: extend the description of CVE patch preparation

  Martin Jansa (13):
        staging.bbclass: process installed dependencies in deterministic order as well
        bitbake.conf: drop ${PE} and ${PR} from -f{file,macro,debug}-prefix-map
        ovmf: drop PE, PR from /usr/src/debug paths
        go-cross-canadian.inc: drop PE, PR from /usr/src/debug paths
        acpica: drop PE, PR from /usr/src/debug paths
        libjpeg-turbo: drop PE, PR from /usr/src/debug paths
        ffmpeg: drop PE, PR from /usr/src/debug paths
        perf: drop PE, PR from /usr/src/debug paths
        rust: drop PE, PR from /usr/src/debug paths
        vulkan-samples: drop PE, PR from /usr/src/debug paths
        valgrind: drop PE, PR from /usr/src/debug paths
        python3-cython: drop PE, PR from /usr/src/debug paths
        igt-gpu-tools: drop PR from /usr/src/debug paths

  Massimiliano Minella (1):
        systemd: update LICENSE statement

  Max Krummenacher (2):
        Revert "bin_package.bbclass: Inhibit the default dependencies"
        perf: fix build with latest kernel

  Meenali Gupta (5):
        avahi: fix CVE-2023-38469
        avahi: fix CVE-2023-38470
        avahi: fix CVE-2023-38471
        avahi: fix CVE-2023-38472
        avahi: fix CVE-2023-38473

  Michael Halstead (1):
        docs: add support for nanbield (4.3) release

  Michael Opdenacker (29):
        manuals: update linux-yocto append examples
        dev-manual: wic: update "wic list images" output
        sdk-manual: appendix-obtain: improve and update descriptions
        manuals: update list of supported machines
        bsp-guide: bsp: skip Intel machines no longer supported in Poky
        brief-yoctoprojectqs: use new CDN mirror for sstate
        dev-manual: start.rst: remove obsolete reference
        local.conf.sample: remove mips edgerouter machine
        oeqa/runtime/cases/parselogs: remove "edgerouter" case
        manuals: correct "yocto-linux" by "linux-yocto"
        test-manual: reproducible-builds: stop mentioning LTO bug
        ref-manual: document KERNEL_LOCALVERSION
        ref-manual: variables: document OEQA_REPRODUCIBLE_TEST_PACKAGE
        migration-guides: updates for 4.3
        migration-guides: mention runqemu change in serial port management
        ref-manual: document KERNEL_STRIP
        migration-guides: further updates for 4.3
        manuals: improve description of CVE_STATUS and CVE_STATUS_GROUPS
        ref-manual: document MESON_TARGET
        ref-manual: document cargo_c class
        ref-manual: variables: mention new CDN for SSTATE_MIRRORS
        ref-manual: variables: add RECIPE_MAINTAINER
        ref-manual: variables: remove SERIAL_CONSOLES_CHECK
        migration-guides: further updates for release 4.3
        bsp-guide: bsp.rst: update beaglebone example
        ref-manual: classes: explain cml1 class name
        migration-guides: fix empty sections
        manuals: fix URL
        ref-manual: releases.svg: update nanbield release status

  Mickael RAMILISON (1):
        scripts/patchreview: Add a custom pattern for finding recipe patches

  Mingli Yu (2):
        openssh: Add sshd.service
        openssh: Don't hardcode the dir in sshd.service

  Niko Mauno (6):
        package_rpm: Fix some pycodestyle issues
        package_rpm: Minor cosmetic and style fixes
        package_rpm: Remove unused definitions
        package_rpm: Allow compression mode override
        image_types.bbclass: Use xz default compression preset level
        ccache.conf: Remove obsolete configuration option

  Paul Barker (1):
        ref-manual: Fix reference to MIRRORS/PREMIRRORS defaults

  Paul Eggleton (12):
        Remove references to apm in MACHINE_FEATURES
        ref-manual: update SDK_NAME variable documentation
        ref-manual: remove semicolons from *PROCESS_COMMAND variables
        release-notes-4.3: fix some typos
        release-notes-4.3: tweaks to existing text
        release-notes-4.3: add CVEs, recipe upgrades, license changes, contributors
        release-notes-4.3: remove the Distribution section
        release-notes-4.3: move new classes to Rust section
        release-notes-4.3: feature additions
        migration-4.3: remove some unnecessary items
        migration-4.3: adjustments to existing text
        migration-4.3: additional migration items

  Pavel Zhukov (1):
        bitbake: tests/fetch.py: Add tests to cover multiple branch/name parameters

  Peter Kjellerstedt (5):
        bb-matrix-plot.sh: Show underscores correctly in labels
        bitbake: command: Make parseRecipeFile() handle virtual recipes correctly
        bitbake: cookerdata: Be consistent with what type bb_data represents
        bitbake: cache: Simplify virtualfn2realfn()
        oeqa/selftest/tinfoil: Add tests that parse virtual recipes

  Peter Marko (1):
        openssl: Upgrade 3.1.3 -> 3.1.4

  Quentin Schulz (2):
        recipes-rt: update README to match newer override syntax
        ref-manual: variables: provide no-match example for COMPATIBLE_MACHINE

  Ragesh Nair (1):
        bitbake: fetch2/git: fix lfs fetch with destsuffix param

  Randy MacLeod (2):
        strace: backport fix for so_peerpidfd-test
        strace: upgrade 6.5 -> 6.6

  Rasmus Villemoes (3):
        perf: lift TARGET_CC_ARCH modification out of security_flags.inc
        valgrind: split helper scripts to separate packages, update dependencies
        perf: add jevents PACKAGECONFIG item

  Richard Purdie (34):
        reproducible: Exclude rust for now again
        linux/cve-exclusion6.1/6.5: Update to latest kernel point releases
        oeqa/qemurunner: Drop newlines serial workaround
        local.conf.sample: Document new CDN mirror for sstate
        poky.conf: Bump version for 4.3 nanbield release
        build-appliance-image: Update to master head revision
        poky.conf: Update to post release versioning
        base: Ensure recipes using mercurial-native have certificates
        qemu: Upgrade 8.1.0 -> 8.1.2
        oeqa/selftest: Drop machines support
        sstate: Ensure sstate searches update file mtime
        insane: Move unpack tests to do_recipe_qa
        go-vendor: Minor style tweaks
        package/package_write: Improve packagedata code location
        debianutils: Fix warnings
        bitbake: runqueue: Fix runall option for setscene tasks
        bitbake: runqueue: Fix errors when using -S printdiff
        oeqa/selftest/sstatetests: Fix intermitttent errors and improve performance
        layer.conf: Switch layer to nanbield series only
        libdnf: Fix arm arch mapping issues for qemuarmv5
        linux/cve-exclusion6.1/6.5: Update to latest kernel point releases
        bitbake: Revert "toaster: Bug-fix webdriver No parameter named options"
        vim: Improve locale handling
        selftest/reproducible: Allow packages exclusion via config
        bitbake: runqueue: Move 'cantskip' into sqdata
        bitbake: runqueue: Refactor StaleSetSceneTasks event out of build_scenequeue_data
        bitbake: toaster/tox.ini: Add py 3.11 and 3.12
        bitbake.conf: Drop oldincludedir
        bitbake: cooker: Add support for BB_DEFAULT_EVENTLOG
        bitbake: cooker: Avoid sideeffects for autorev from getAllKeysWithFlags
        oeqa/selftest/sstatetests: Re-enable CDN tests
        bitbake.conf: Log events by default using BB_DEFAULT_EVENTLOG
        package_ipk: Fix Source: field variable dependency
        Revert "binutils: Fix CVE-2022-47007"

  Robert P. J. Day (2):
        dev-manual: new-recipe.rst: add missing parenthesis to "Patching Code" section
        profile-manual: aesthetic cleanups

  Ross Burton (36):
        man-db: add RRECOMMENDS on glibc-utils for iconv
        man-db: remove inexplicable man_db.conf patch
        patchtest: remove unused imports
        patchtest: sort when reading patches from a directory
        linux-yocto: update CVE exclusions
        libxml2: ignore disputed CVE-2023-45322
        zlib: ignore CVE-2023-45853
        cve-check: sort the package list in the JSON report
        cve-check: slightly more verbose warning when adding the same package twice
        pixman: ignore CVE-2023-37769
        scripts/patchreview: rework patch detection
        scripts/contrib/patchreview: add commit and recipe count fields to JSON
        scripts/contrib/patchreview: consolidate imports
        scripts/contrib/patchreview: fix commit identification
        cve-check: don't warn if a patch is remote
        migration-guides: add debian 12 to newly supported distros
        migration-guides: edgerouter machine removed
        migration-guides: QEMU_USE_SLIRP variable removed
        migration-guides: remove non-notable change
        migration-guides: mention LLVM 17
        migration-guides: mention CDN
        migration-guides: add kernel notes
        migration-guides: remove SERIAL_CONSOLES_CHECK
        migration-guides: enabling SPDX only for Poky, not a global default
        migration-guides: add testing notes
        migration-guides: add utility notes
        migration-guides: add BitBake changes
        migration-guides: packaging changes
        migration-guides: git recipes reword
        poky-tiny: fix PACKAGE_EXCLUDE
        Revert "xserver-xorg: Fix for CVE-2023-5574"
        xwayland: upgrade to 23.2.2
        lib/oe/patch: ensure os.chdir restoring always happens
        oeqa/selftest/debuginfod: improve selftest
        shared-mime-info: embed PV in the filename
        rust-llvm: remove python3native dependency

  Rouven Czerwinski (1):
        glib-2.0: Remove unnecessary assignement

  Sean Nyekjaer (3):
        rust-cross-canadian: set CARGO_TARGET_<triple>_RUSTFLAGS
        rust-cross-canadian: set CARGO_TARGET_<triple>_RUNNER for nativesdk
        oeqa/sdk/rust: Add build and run test of rust binary with SDK host

  Sergei Zhmylev (1):
        classes: Move package RDEPENDS processing out of debian.bbclass

  Siddharth Doshi (2):
        vim: Upgrade 9.0.1894 -> 9.0.2009
        vim: Upgrade 9.0.2009 -> 9.0.2048

  Stefan Herbrechtsmeier (2):
        glibc: use nonarch libdir for tmpfiles.d
        classes: go-mod: do not pack go mod cache

  Steve Sakoman (1):
        vim: use upstream generated .po files

  Stéphane Veyret (2):
        volatile-binds: Allow creation of subdirectories
        volatile-binds: Calculate the name of the /var/lib service

  Thomas Perrot (1):
        opensbi: Upgrade to 1.3.1 release

  Thomas Wolber (1):
        kea: drop unused directory

  Tim Orling (9):
        recipetool: add python_hatchling support
        lsb-release: use https for UPSTREAM_CHECK_URI
        bitbake: toaster: drop deprecated USE_L10N from settings
        bitbake: toaster: use docs for BitBake link on landing page
        bitbake: toaster: fix obsolete use of find_element_by_link_text
        bitbake: toaster: test_create_new_project typos, whitespace
        python3-hypothesis: upgrade 6.88.3 -> 6.89.0
        python3-setuptools-scm: upgrade 7.1.0 -> 8.0.4
        python3-poetry-core: upgrade 1.7.0 -> 1.8.1

  Trevor Gamblin (30):
        patchtest: improve test issue messages
        patchtest: clean up test suite
        patchtest/requirements.txt: update
        patchtest: add supporting modules
        patchtest: add scripts to oe-core
        patchtest: set default repo and testdir targets
        patchtest: update SPDX identifiers
        patchtest/selftest: fix command arguments
        patchtest: check for untracked changes
        patchtest: test regardless of mergeability
        patchtest: skip merge test if not targeting master
        contributor-guide: add patchtest section
        contributor-guide: clarify patchtest usage
        patchtest: fix lic_files_chksum test regex
        patchtest-send-results: improve subject line
        patchtest: disable merge test
        patchtest-send-results: check max line length, simplify responses
        patchtest/selftest: add XSKIP, update test files
        patchtest: simplify test directory structure
        patchtest: reduce checksum test output length
        patchtest: shorten test result outputs
        patchtest-send-results: send results to submitter
        patchtest-send-results: add In-Reply-To
        patchtest: make pylint tests compatible with 3.x
        patchtest: remove test for CVE tag in mbox
        patchtest-send-results: fix sender parsing
        patchtest: rework license checksum tests
        python3-mako: upgrade 1.2.4 -> 1.3.0
        python3-trove-classifiers: upgrade 2023.10.18 -> 2023.11.14
        python3-numpy: upgrade 1.26.0 -> 1.26.2

  Vijay Anusuri (1):
        xserver-xorg: Fix for CVE-2023-5574

  Vincent Davis Jr (1):
        acpica: add nativesdk to BBCLASSEXTEND

  Vyacheslav Yurkov (1):
        lib/oe/path: Deploy files can start only with a dot

  Wang Mingyu (79):
        openssh: upgrade 9.4p1 -> 9.5p1
        bluez5: upgrade 5.69 -> 5.70
        btrfs-tools: upgrade 6.5.1 -> 6.5.2
        createrepo-c: upgrade 1.0.0 -> 1.0.1
        dhcpcd: upgrade 10.0.2 -> 10.0.3
        ell: upgrade 0.58 -> 0.59
        kmod: upgrade 30 -> 31
        libcomps: upgrade 0.1.19 -> 0.1.20
        libsdl2: upgrade 2.28.3 -> 2.28.4
        libubootenv: upgrade 0.3.4 -> 0.3.5
        ltp: upgrade 20230516 -> 20230929
        libva: upgrade 2.19.0 -> 2.20.0
        python3-git: upgrade 3.1.36 -> 3.1.37
        python3-babel: upgrade 2.12.1 -> 2.13.0
        python3-beartype: upgrade 0.15.0 -> 0.16.2
        python3-cffi: upgrade 1.15.1 -> 1.16.0
        python3-hypothesis: upgrade 6.86.2 -> 6.87.4
        python3-iso8601: upgrade 2.0.0 -> 2.1.0
        python3-markdown: upgrade 3.4.4 -> 3.5
        python3-packaging: upgrade 23.1 -> 23.2
        python3-pycairo: upgrade 1.24.0 -> 1.25.0
        python3-ruamel-yaml: upgrade 0.17.32 -> 0.17.35
        xkeyboard-config: upgrade 2.39 -> 2.40
        python3-wcwidth: upgrade 0.2.6 -> 0.2.8
        repo: upgrade 2.36.1 -> 2.37
        shared-mime-info: upgrade 2.2 -> 2.3
        sqlite3: upgrade 3.43.1 -> 3.43.2
        stress-ng: upgrade 0.16.05 -> 0.17.00
        base-passwd: upgrade 3.6.1 -> 3.6.2
        createrepo-c: upgrade 1.0.1 -> 1.0.2
        cronie: upgrade 1.6.1 -> 1.7.0
        dhcpcd: upgrade 10.0.3 -> 10.0.4
        enchant2: upgrade 2.6.1 -> 2.6.2
        btrfs-tools: upgrade 6.5.2 -> 6.5.3
        debianutils: upgrade 5.13 -> 5.14
        gpgme: upgrade 1.22.0 -> 1.23.1
        harfbuzz: upgrade 8.2.1 -> 8.2.2
        libdnf: upgrade 0.71.0 -> 0.72.0
        libical: upgrade 3.0.16 -> 3.0.17
        libjpeg-turbo: upgrade 3.0.0 -> 3.0.1
        libnewt: upgrade 0.52.23 -> 0.52.24
        libnsl2: upgrade 2.0.0 -> 2.0.1
        lighttpd: upgrade 1.4.72 -> 1.4.73
        msmtp: upgrade 1.8.24 -> 1.8.25
        ghostscript: upgrade 10.02.0 -> 10.02.1
        glib-2.0: upgrade 2.78.0 -> 2.78.1
        python3-pyrsistent: upgrade 0.19.3 -> 0.20.0
        python3-babel: upgrade 2.13.0 -> 2.13.1
        python3-gitdb: upgrade 4.0.10 -> 4.0.11
        python3-git: upgrade 3.1.37 -> 3.1.40
        python3-hypothesis: upgrade 6.87.4 -> 6.88.1
        python3-pip: upgrade 23.2.1 -> 23.3.1
        python3-psutil: upgrade 5.9.5 -> 5.9.6
        python3-pycairo: upgrade 1.25.0 -> 1.25.1
        python3-pyopenssl: upgrade 23.2.0 -> 23.3.0
        python3-pytest: upgrade 7.4.2 -> 7.4.3
        python3-setuptools-rust: upgrade 1.7.0 -> 1.8.1
        python3-testtools: upgrade 2.6.0 -> 2.7.0
        python3-trove-classifiers: upgrade 2023.9.19 -> 2023.10.18
        python3-wcwidth: upgrade 0.2.8 -> 0.2.9
        python3-wheel: upgrade 0.41.2 -> 0.41.3
        shaderc: upgrade 2023.6 -> 2023.7
        xserver-xorg: upgrade 21.1.8 -> 21.1.9
        python3-cryptography(-vectors): upgrade 41.0.4 -> 41.0.5
        dhcpcd: upgrade 10.0.4 -> 10.0.5
        diffoscope: upgrade 249 -> 251
        git: upgrade 2.42.0 -> 2.42.1
        iproute2: upgrade 6.5.0 -> 6.6.0
        libsdl2: upgrade 2.28.4 -> 2.28.5
        libsolv: upgrade 0.7.25 -> 0.7.26
        libuv: upgrade 1.46.0 -> 1.47.0
        bash: upgrade 5.2.15 -> 5.2.21
        dnf: upgrade 4.17.0 -> 4.18.1
        python3-hatch-vcs: upgrade 0.3.0 -> 0.4.0
        python3-hypothesis: upgrade 6.88.1 -> 6.88.3
        python3-pbr: upgrade 5.11.1 -> 6.0.0
        python3-testtools: upgrade 2.7.0 -> 2.7.1
        shared-mime-info: upgrade 2.3 -> 2.4
        stress-ng: upgrade 0.17.00 -> 0.17.01

  William A. Kennington III (1):
        kernel: Commit without running hooks

  William Lyu (2):
        perl: fix intermittent test failure
        openssl: improve handshake test error reporting

  Xiangyu Chen (4):
        linux-yocto: make sure the pahole-native available before do_kernel_configme
        grub: Fix for CVE-2023-4692 and CVE-2023-4693
        sudo: upgrade 1.9.14p3 -> 1.9.15p2
        openssh: add systemd readiness notification support

  Yoann Congal (4):
        insane: skip unimplemented-ptest on S=WORKDIR recipes
        insane: unimplemented-ptest: ignore source file errors
        selftest/reproducible: Split a long line
        meta-selftest/files: add xuser to static-passwd/-group

  david d zuhn (1):
        bitbake.conf: remove ${CCACHE} from FORTRAN compiler

  luca fancellu (1):
        oeqa/ssh: Handle SSHCall timeout error code

meta-arm: e914891eee..1dff3300fb:
  Abdellatif El Khlifi (6):
        arm-bsp/linux-yocto: corstone1000: bump to v6.5%
        arm-bsp/documentation: corstone1000: enable debug-tweaks
        arm-bsp/documentation: corstone1000: update the release note
        arm-bsp/documentation: corstone1000: update the change log
        arm-bsp/documentation: corstone1000: update the user guide
        kas: corstone1000: pin the SHAs

  Ali Can Ozaslan (1):
        arm-bsp/documentation: corstone1000: Update the user guide

  Debbie Martin (10):
        arm-bsp/u-boot: Divide the U-boot configuration by machine
        arm-bsp/fvp-base: Merge fvp-common.inc into fvp-base.conf
        arm-bsp/trusted-firmware-a/fvp-base: Add stdout path and virtio net and rng
        arm-bsp/u-boot/fvp-base: Configure FVP base U-boot machine and enable U-boot sysreset, CRC-32 and virtio RNG
        arm-bsp/fvp-base: Configure grub as the EFI provider
        arm/fvp-base: Update the default testsuites
        arm-systemready: Introduce the Arm SystemReady layer
        arm-bsp/systemready: Bring up the Arm SystemReady IR ACS 2.0 suite on FVP base
        kas: Add kas configuration for Arm SystemReady and fvp-base
        ci: Add fvpboot to IMAGE_CLASSES

  Delane Brandy (1):
        arm-bsp/documentation: corstone1000: Update the user guide

  Drew Reed (2):
        arm-bsp: Enable TF-A test building for the N1SDP
        CI: Enable TF-A TFTF test builds

  Emekcan Aras (17):
        arm-bsp/u-boot: corstone1000: enable on-disk capsule update
        arm-bsp/u-boot: corstone1000: fix runtime capsule update flag checks
        arm-bsp/trusted-firmware-m: fix capsule update alignment
        arm-bsp/trusted-firmware-m: update the upstream status of the out-of-tree patches
        arm-bsp/u-boot: corstone1000: scatter gather list workaround for ondisk capsule update
        arm-bsp/trusted-services: enable signaled handling interrupts for SPs
        arm-bsp/corstone1000: fix synchronization issue on openamp notification
        arm/fvp-corstone1000: upgrade to 11.23_25
        arm-bsp/corstone1000-fvp: Add virtio-net configuration
        arm-bsp/corstone1000-fvp: add unpadded image support for MMC card config
        arm-bsp/corstone1000-fvp: Disable Time Annotation
        arm-bsp/u-boot: corstone1000: enable virtio-net support for FVP
        arm-bsp/documentation: corstone1000: update the architecture document
        arm-bsp/documentation: corstone1000: Add EFI system partition section
        arm-bsp/documentation: corstone1000: add a note and fix instructions
        arm-bsp/documentation: corstone1000: add readthedocs.yaml file
        arm-bsp/documentation: corstone1000: fix the requirements.txt and conf.py path

  Harsimran Singh Tungal (4):
        arm-bsp/u-boot: corstone1000: Remove External system patches
        arm-bsp/linux: corstone1000: update the defconfig
        arm-bsp/linux: corstone1000: Remove External system patches
        arm-bsp/images: corstone1000: Remove the external system test package

  Javier Tia (1):
        trusted-firmware-a: fix build error when using ccache

  Jon Mason (10):
        arm-bsp/linux-yocto: add recipe for v6.4 kernel
        arm/linux-yocto: remove defconfig patch
        CI: add sbsa-acs to recipe report
        arm/linux-yocto: remove PHYS_VIRT config frag
        arm-bsp/optee: remove 3.18 recipes and patches
        arm-bsp/edk2: remove 202211
        arm/hafnium: update to v2.9
        arm/optee: update to 4.0.0
        arm/optee: cleanups from code review
        arm/toolchains: update to 13.2.Rel1

  Mariam Elshakfy (3):
        arm-bsp/n1sdp: Move OP-TEE to DDR4
        arm-bsp/n1sdp: Enable OP-TEE cache in N1SDP
        arm-bsp/corstone1000: Remove inappropriate kernel delay patch

  Ross Burton (24):
        arm/oeqa/selftest: tag all tests with "meta-arm"
        CI: don't hardcode the selftest tests to run
        CI: also run the _qemutiny testcase for poky-tiny
        CI: track nanbield branches
        arm/fvp-corstone1000: upgrade to 11.22.35, add aarch64 binaries
        kas/corstone1000: don't limit the FVP use to x86-64
        CI: don't pin corstone1000-fvp to x86-64
        CI: build both aarch64 and x86-64 packages for as many FVPs as possible
        arm-bsp/u-boot: remove 2023.01
        arm/trusted-firmware-a: update mbedtls to recommended release
        CI: Add meta-secure-core to pending-upgrades for corstone1000
        arm-bsp: corstone1000 depends on meta-efi-secure-boot
        arm/generic-arm64: remove obsolete SERIAL_CONSOLES_CHECK
        arm/lib/fvp/runner: don't pass '' as cwd
        scripts/runfvp: exit code should be the FVP exit code
        arm/selftest: add test that DISPLAY is forwarded into the runfvp child
        CI: use nanbield branch for meta-virtualization
        CI: use nanbield branch of meta-clang
        arm/optee: handle CVE-2021-36133 as disputed
        arm-bsp/optee-os: backport fix for CVE-2023-41325
        arm/fvp-base-a-aem: upgrade to 11.23.9
        arm-bsp/fvp-base: upgrade tune to v8.4
        arm-bsp/trusted-firmware-a: use v8.4 instructions on fvp-base
        arm-bsp/optee-os: update Upstream-Status tags

  Vikas Katariya (1):
        arm-bsp/corstone1000: Fix RSA key generation issue

  Xueliang Zhong (2):
        Update Corstone-1000 doc with security issue reporting guideline
        arm-bsp/n1sdp: update to linux yocto kernel 6.5

meta-raspberrypi: 482d864b8f..8231f97534:
  Andrei Gherzan (1):
        docs: Fix ReadTheDocs builds.os requirement

  Carlos Alberto Lopez Perez (1):
        linux-raspberrypi: stop setting powersave as the default CPU governor

  Jose Quaresma (2):
        linux-raspberrypi/linux-raspberrypi-v7: drop 5.10 version
        rpi-base: Adds EXTRA_IMAGEDEPENDS to fix the image task do_populate_lic_deploy

  Khem Raj (1):
        linux-raspberrypi_6.1.bb: Update to 6.1.61 release

  Leon Anavi (2):
        rpi-config: Upgrade to tip of tree
        rpi-config: reintroduce start_x

  Matthew Draws (1):
        rpi-eeprom: Update to 2023.10.18-2712

  Vincent Davis Jr (1):
        rpidistro-vlc: add new patch po-Fix-typos-in-oc

meta-openembedded: 62039a2c33..991e6852a5:
  Akash Hadke (1):
        libeigen: Update GPL-3.0-only to GPL-2.0-only

  Alex Kiernan (2):
        reptyr: Add 0.10.0
        mdns: Upgrade 2200.0.8 -> 2200.40.37.0.1

  Alper Ak (1):
        unionfs-fuse: upgrade 2.2 --> 3.4

  Andrew Jeffery (1):
        mdio-tools: Add virtual/kernel dependency to avoid stale SPDX reference

  Armin Kuster (4):
        netkit: Drop old and no upstream
        MAINTANERS: drop netkit
        README: drop netkit maintainer
        pkggrp: drop netkit

  Arthur Oliveira (5):
        python3-objectpath: Add ObjectPath Python Recipe
        python3-flask-restx: Add Flask-RestX Python Recipe
        python3-zopeevent: Add Zope.Event Python Recipe
        python3-aniso8601: Add ISO 8601 parsing library
        python3-flask-restx: Switch dependency from isodate to aniso8601

  Bartosz Golaszewski (5):
        shunit2: new recipe
        libgpiod: update to v2.1
        python3-gpiod: update to v2.1.3
        python3-gpiod: setup target config in ptest compile
        python3-gpiod: fix the required version of libgpiod

  Beniamin Sandu (2):
        mbedtls: upgrade 3.4.1 -> 3.5.0
        unbound: upgrade 1.18.0 -> 1.19.0

  Benjamin Bouvier (1):
        libsmi: enable native build

  Carlos Alberto Lopez Perez (1):
        libbacktrace: Update version and enable shared library.

  Charles Perry (4):
        libosip2: add recipe
        libexosip2: add recipe
        libexosip2: add c-ares and openssl PACKAGECONFIG
        libexosip2: package binaries in a separate package

  Chi Xu (1):
        re2: Add ptest support

  Christian Eggers (1):
        python3-gcovr: switch to main branch

  Christophe Vu-Brugier (1):
        exfatprogs: upgrade 1.2.1 -> 1.2.2

  Clément Péron (2):
        proj: Upgrade to 9.3.0 release
        pcapplusplus: Add recipe for 23.09 release

  Daiane Angolini (1):
        wireguard-tools: Use PACKAGECONFIG to select wg-quick and bash-completion

  Daniel McGregor (1):
        python3-pylint: allow native build

  Daniel Semkowicz (2):
        cockpit: Fix cockpit-askpass path
        cockpit: Bump to version 304

  David Pierret (3):
        libtext: add ptest
        cjson: Add ptest
        python3-rapidjson: add missing ptest dependency

  Edi Feschiyan (1):
        libbytesize: update SRC_URI

  Etienne Cordonnier (1):
        uutils-coreutils: upgrade 0.0.21 -> 0.0.22

  Fabien Thomas (2):
        klibc/klibc.inc : Add DEBUG_PREFIX_MAP flag.
        samba.bb : Disable ad-dc by default

  Fabio Estevam (5):
        edid-decode: Upgrade to latest master
        openocd: Use https for github
        python3-piccata: Use https for github
        multipath-tools: Use https for github
        crucible: Upgrade to 2023.11.02

  Gianfranco Costamagna (3):
        vbxguestdrivers: upgrade 7.0.10 -> 7.0.12
        cpulimit: add DESCRIPTION field
        dlt-daemon: cherry-pick another upstream-proposed patch

  Hains van den Bosch (1):
        libebml: Enable shared libraries

  Jamin Lin (1):
        Brotli: fix build failed if the path includes "-static"

  Jan Claußen (1):
        btop: Add recipe

  Jan Vermaete (3):
        netdata: chown in systemd service with ':' iso '.'
        netdata: version bump 1.43.0 -> 1.43.2
        README.md: was a Markdown paragraph and should be a list

  Jeffrey Pautler (1):
        apache2: add vendor to product name used for CVE checking

  Joe Slater (2):
        python3-pynacl: add RCONFLICTS with python3-nacl
        python3-django: move to version 4.2.5

  Johannes Kauffmann (1):
        open62541: update to v1.3.8

  Johnathan Mantey (1):
        ipmitool: Update and eliminate unneeded patch

  Jonas Gorski (1):
        frr: fix CVEs CVE-2023-4675{2,3} and CVE-2023-4723{4,5}

  Jose Quaresma (4):
        ostree: Upgrade 2023.5 -> 2023.6
        ostree: drop trivial-httpd-cmdline
        ostree: add ed25519-openssl
        ostree: Upgrade 2023.6 -> 2023.7

  Kai Kang (4):
        xfce4-panel-profiles: 1.0.13 -> 1.0.14
        python3-nacl: drop duplicate recipe
        python3-blivet: 3.4.3 -> 3.8.2
        python3-blivetgui: 2.3.0 -> 2.4.2

  Khem Raj (209):
        libnet-idn-encode: Fix build with perl 2.38 and gcc13
        poco: Fix data race when create POSIX thread
        static-group: Match nogroup id to base-passwd from core.
        gutenprint: Upgrade to 5.3.4
        meta-perl: Add libtext-diff-perl to fast ptest list
        leveldb: Upgrade to 1.23 plus latest git
        meta-python: Add python3-rapidjson to PTESTS_FAST_META_PYTHON
        leveldb: Print uint64_t with PRI64
        network-manager-applet,networkmanager-openvpn, networkmanager: Apply linker versioning patch when using lld only
        emlog: Add PV
        ccid: upgrade 1.5.2 -> 1.5.4
        jack: upgrade 1.19.22 -> 2
        abseil-cpp: upgrade 20230802.0 -> 20230802.1
        xterm: upgrade 387 -> 388
        toybox: upgrade 0.8.8 -> 0.8.10
        pahole: upgrade 1.24 -> 1.25
        gcab: upgrade 1.4 -> 1.6
        feh: upgrade 3.10 -> 3.10.1
        xmlsec1: upgrade 1.2.37 -> 1.3.2
        xmlsec1: Fix the key name in verify2 test
        ctags: upgrade 6.0.20231001.0 -> 6.0.20231029.0
        googlebenchmark: upgrade 1.8.0 -> 1.8.3
        opencl-headers: upgrade 04.17 -> 2023.04.17
        thingsboard-gateway: upgrade 3.4.1 -> 3.4.2
        neatvnc: upgrade 0.6.0 -> 0.7.0
        lastlog2: upgrade 1.1.0 -> 1.2.0
        libmbim: upgrade 1.30.0 -> 1.31.1
        ser2net: upgrade 4.3.13 -> 4.5.0
        fio: upgrade 3.32 -> 2022
        libosinfo: upgrade 1.10 -> 1.11.0
        webkitgtk3: upgrade 2.42.0 -> 2.42.1
        mstpd: upgrade 0.1 -> 0.05
        smarty: upgrade 4.3.0 -> 4.3.4
        geos: upgrade 3.12.0 -> 3.12.0beta2
        wtmpdb: upgrade 0.7.1 -> 0.9.3
        lsscsi: upgrade 0.32 -> 030
        glibmm-2.68: upgrade 2.74.0 -> 2.78.0
        mcelog: upgrade 194 -> 196
        libfastjson: upgrade 0.99.9 -> 1.2304.0
        libraw: upgrade 0.20.2 -> 0.21.1
        cairomm-1.16: upgrade 1.16.2 -> 1.18.0
        libbpf: upgrade 1.2.0 -> 1.2.2
        libtorrent: upgrade 0.13.8 -> 1
        modemmanager: upgrade 1.22.0 -> 1.23.1
        c-ares: upgrade 1.20.1 -> 1.21.0
        pmdk: upgrade 1.12.1 -> 2.0.0
        hwdata: upgrade 0.370 -> 0.375
        mksh: upgrade 59 -> R59c
        sdbus-c++: upgrade 1.3.0 -> 1.4.0
        cjson: upgrade 1.7.15 -> 1.7.16
        uftrace: upgrade 0.13.1 -> 0.14
        python3-trustme: upgrade 0.9.0 -> 1.1.0
        python3-eth-utils: upgrade 2.2.2 -> 2.3.0
        python3-xstatic-font-awesome: upgrade 4.7.0.0 -> 6.2.1.1
        python3-process-tests: upgrade 2.1.2 -> 3.0.0
        python3-pyperf: upgrade 2.6.1 -> 2.6.2
        python3-sentry-sdk: upgrade 1.26.0 -> 1.34.0
        python3-websockets: upgrade 11.0.3 -> 12.0
        python3-alembic: upgrade 1.12.0 -> 1.12.1
        python3-pymisp: upgrade 2.4.176 -> 2.4.178
        python3-traitlets: upgrade 5.11.2 -> 5.13.0
        python3-pytest-mock: upgrade 3.11.1 -> 3.12.0
        python3-kivy: upgrade 2.1.0 -> 2.2.1
        python3-web3: upgrade 6.11.1 -> 6.11.2
        python3-m2crypto: upgrade 0.39.0 -> 0.40.1
        python3-rapidjson: upgrade 1.12 -> 1.13
        python3-eth-typing: upgrade 3.5.0 -> 3.5.1
        python3-email-validator: upgrade 2.0.0 -> 2.1.0
        python3-icu: upgrade 2.11 -> 2.12
        python3-virtualenv: upgrade 20.24.5 -> 20.24.6
        python3-tzlocal: upgrade 5.1 -> 5.2
        python3-cantools: upgrade 39.2.0 -> 39.3.0
        python3-flask-login: upgrade 0.6.2 -> 0.6.3
        python3-argcomplete: upgrade 3.1.2 -> 3.1.4
        python3-wxgtk4: upgrade 4.2.0 -> 4.2.1
        python3-meson-python: upgrade 0.14.0 -> 0.15.0
        python3-pymongo: upgrade 4.5.0 -> 4.6.0
        python3-imgtool: upgrade 1.10.0 -> 2.0.0
        python3-google-api-python-client: upgrade 2.104.0 -> 2.106.0
        python3-tornado: upgrade 6.3 -> 6.3.3
        python3-imageio: upgrade 2.31.5 -> 2.31.6
        python3-blinker: upgrade 1.6.3 -> 1.7.0
        python3-pyhamcrest: upgrade 2.0.4 -> 2.1.0
        python3-pytest-asyncio: upgrade 0.21.1 -> 0.22.0
        python3-pyjwt: upgrade 2.7.0 -> 2.8.0
        python3-bitstruct: upgrade 8.18.0 -> 8.19.0
        python3-filelock: upgrade 3.12.4 -> 3.13.1
        python3-sqlalchemy: upgrade 2.0.22 -> 2.0.23
        python3-greenlet: upgrade 2.0.2 -> 3.0.1
        python3-charset-normalizer: upgrade 3.3.0 -> 3.3.2
        python3-cbor2: upgrade 5.4.6 -> 5.5.1
        python3-cbor2: Add missing hypothesis rdep for ptests
        python3-asttokens: upgrade 2.4.0 -> 2.4.1
        python3-xlsxwriter: upgrade 3.1.8 -> 3.1.9
        python3-cachetools: upgrade 5.3.1 -> 5.3.2
        python3-paramiko: upgrade 3.2.0 -> 3.3.1
        python3-tomlkit: upgrade 0.12.1 -> 0.12.2
        python3-eth-account: upgrade 0.9.0 -> 0.10.0
        python3-reedsolo: upgrade 1.7.0 -> 2.0.13
        python3-shellingham: upgrade 1.5.3 -> 1.5.4
        python3-ipython: upgrade 8.16.1 -> 8.17.2
        python3-argh: upgrade 0.29.4 -> 0.30.3
        python3-executing: upgrade 2.0.0 -> 2.0.1
        python3-pylint: upgrade 3.0.1 -> 3.0.2
        python3-google-auth: upgrade 2.23.3 -> 2.23.4
        libtest-harness-perl: upgrade 3.47 -> 3.48
        libmodule-build-tiny-perl: upgrade 0.046 -> 0.047
        libdbd-sqlite-perl: upgrade 1.72 -> 1.74
        libconfig-tiny-perl: upgrade 2.29 -> 2.30
        libcgi-perl: upgrade 4.57 -> 4.60
        ipset: upgrade 7.15 -> 7.19
        openvpn: upgrade 2.6.3 -> 2.6.6
        nng: upgrade 1.5.2 -> 12
        usrsctp: upgrade to latest revision
        python3-scapy: upgrade to latest revision
        wolfssl: upgrade 5.5.4 -> 5.6.4
        tnftp: upgrade 20210827 -> 20230507
        fluidsynth: upgrade 2.3.2 -> 2.3.4
        libuvc: upgrade 0.0.6 -> 0.0.7
        libdc1394: upgrade 2.2.6 -> 2.2.7
        ncmpc: upgrade 0.47 -> 0.49
        gerbera: upgrade 1.11.0 -> 1.12.1
        gst-shark: upgrade 0.7.3.1 -> 0.8.1
        gupnp-av: upgrade 0.14.0 -> 0.14.1
        libmediaart-2.0: upgrade 1.9.5 -> 1.9.6
        libdvbpsi: upgrade 1.3.0 -> 1.3.3
        fdk-aac: upgrade 2.0.1 -> 2.0.2
        libavif: upgrade 0.11.1 -> 1.0.1
        libdvdcss: upgrade 1.4.2 -> 1.4.3
        aom: upgrade 3.6.1 -> 3.7.0
        aom: Disable neon when building on arm
        dav1d: upgrade 1.2.0 -> 1.3.0
        network-manager-applet: upgrade 1.32.0 -> 1.34.0
        gvfs: upgrade 1.52.0 -> 1.52.1
        gnome-text-editor: upgrade 45.0 -> 45.1
        libwacom: upgrade 2.6.0 -> 2.8.0
        evolution-data-server: upgrade 3.50.0 -> 3.50.1
        orage: upgrade 4.16.0 -> 4.18.0
        xfce4-systemload-plugin: upgrade 1.3.1 -> 1.3.2
        xfce4-screenshooter: upgrade 1.10.3 -> 1.10.4
        xfce4-appfinder: upgrade 4.18.0 -> 4.19.1
        xfce4-netload-plugin: upgrade 1.4.0 -> 1.4.1
        thunar-shares-plugin: upgrade 0.3.1 -> 0.3.2
        xfce4-battery-plugin: upgrade 1.1.4 -> 1.1.5
        xfce4-places-plugin: upgrade 1.8.1 -> 1.8.3
        libxfce4util: upgrade 4.18.1 -> 4.19.2
        xfce4-notes-plugin: upgrade 1.9.0 -> 1.10.0
        xfce4-weather-plugin: upgrade 0.11.0 -> 0.11.1
        thunar: upgrade 4.18.4 -> 4.19.0
        catfish: upgrade 4.16.3 -> 4.18.0
        xfce4-time-out-plugin: upgrade 1.1.2 -> 1.1.3
        thunar-archive-plugin: upgrade 0.5.1 -> 0.5.2
        xfce4-timer-plugin: upgrade 1.7.1 -> 1.7.2
        xfce4-calculator-plugin: upgrade 0.7.1 -> 0.7.2
        xfmpc: upgrade 0.3.0 -> 0.3.1
        garcon: upgrade 4.18.1 -> 4.19.0
        xfce4-genmon-plugin: upgrade 4.1.1 -> 4.2.0
        xfce4-fsguard-plugin: upgrade 1.1.2 -> 1.1.3
        xfce4-cpugraph-plugin: upgrade 1.2.7 -> 1.2.8
        parole: upgrade 4.16.0 -> 4.18.0
        xfce4-datetime-plugin: upgrade 0.8.1 -> 0.8.3
        menulibre: upgrade 2.2.3 -> 2.3.2
        xfce4-pulseaudio-plugin: upgrade 0.4.3 -> 0.4.8
        libxfce4ui: upgrade 4.18.3 -> 4.19.3
        xfce4-taskmanager: upgrade 1.5.5 -> 1.5.6
        xfce4-mpc-plugin: upgrade 0.5.2 -> 0.5.3
        mousepad: upgrade 0.5.9 -> 0.6.1
        gigolo: upgrade 0.5.2 -> 0.5.3
        xfce4-verve-plugin: upgrade 2.0.1 -> 2.0.3
        exo: upgrade 4.18.0 -> 4.19.0
        xfce4-mailwatch-plugin: upgrade 1.3.0 -> 1.3.1
        xarchiver: upgrade 0.5.4.17 -> 0.5.4.21
        xfsprogs: upgrade 6.1.1 -> 6.5.0
        xfstests: upgrade 2023.03.05 -> 2023.10.29
        xfstests: Fix build with clang17
        xfstests: Fix build on musl
        ufs-utils: upgrade to latest revision
        xfce4-systemload-plugin: Fix build on 32bit machines
        libsodium: upgrade 1.0.18 -> 1.0.19
        libsodium: Fix build with clang on aarch64
        Revert "modemmanager: upgrade 1.22.0 -> 1.23.1"
        modemmanager: inherit upstream-version-is-even
        Revert "geos: upgrade 3.12.0 -> 3.12.0beta2"
        emlog: Drop SRCPV
        makedumpfile: Change COMPATIBLE_HOST check to exclude unsupported arches
        packagegroup-meta-oe: Update makedumpfile architecture support list
        gupnp: Add missing rdep on python3-core
        vte9: Upgrade to 0.74.1
        rygel: Upgrade to 0.40.4 -> 0.42.4
        vte9: Add knob for enabling systemd
        meta-networking: Use autotools make system
        meta-oe: Use autotools make system
        toscoterm: Skip recipe, slated for removal
        loudmouth: Upgrade to 1.5.4
        toscoterm: Delete recipe
        librest: Use autotools make system
        cannelloni: Fix build with clang and libc++ runtime
        gnome-console: Add missing dependency on gtk4-native
        gnome-terminal: Add missing dependency on libhandy
        dleyna-core: Update to tip of master
        dleyna: Skip all dleyna recipes, slated for removal
        packagegroup-meta-multimedia: Remove dleyna recipes
        beep: Upgrade to 1.4.12
        yelp: Use autotools for build system
        gstd: Upgrade to 0.15.0
        gimp: Update to 2.10.36
        projucer: Refresh patch to apply cleanly
        ledmon: Fix systemd unit install
        libxml++-5.0: Make use of gnomebase bbclass

  LI Qingwu (1):
        kmsxx: Add recipe

  Lei Maohui (1):
        gexiv2: Fix do_package QA issue when usrmerge enabled.

  Leon Anavi (32):
        sip: upgrade 6.7.11 -> 6.7.12
        python3-rarfile: add recipe
        python3-colorclass: add recipe
        python3-inflate64: add recipe
        python3-jsbeautifier: add recipe
        python3-pymemcache: add recipe
        python3-multivolumefile: add recipe
        python3-oletools: add recipe
        python3-olefile: add recipe
        python3-pcodedmp: add recipe
        python3-screeninfo: add recipe
        python3-unoconv: add recipe
        python3-pybcj: add recipe
        python3-pyppmd: add recipe
        python3-py7zr: add recipe
        python3-wand: add recipe
        python3-pdm-backend: add recipe
        python3-pdm: add recipe
        python3-jsonref: Upgrade 1.0.1 -> 1.1.0
        imlib2: Upgrade 1.7.1 -> 1.12.1
        libblockdev: Upgrade 3.0.3 -> 3.0.4
        exiftool: add recipe
        bindfs: add recipe
        qpdf: Update 10.6.3 -> 11.6.3
        python3-file-magic: add recipe
        python3-wrapt: Upgrade 1.15.0 -> 1.16.0
        python3-bitarray: Upgrade 2.8.2 -> 2.8.3
        python3-pillow: Upgrade 10.0.1 -> 10.1.0
        python3-polyline: upgrade 1.4.0 -> 2.0.1
        python3-py7zr: Upgrade 0.20.7 -> 0.20.8
        python3-zeroconf: upgrade 0.120.0 -> 0.126.0
        python3-pystemd: upgrade 0.10.0 -> 0.13.2

  Luca Fancellu (5):
        linuxptp: update linuxptp recipe to 4.1
        linuxptp: install default configuration file in sysconfdir
        linuxptp: add systemd services
        linuxptp: Drop unneeded downstream patches
        linuxptp: Use templates for the systemd services

  Marek Vasut (2):
        lvgl: lv-drivers: Allow empty package
        lvgl: Allow empty package

  Markus Fuchs (1):
        remove unused AUTHOR variable

  Markus Volk (52):
        libdecor: Upgrade 0.1.99 -> 0.2.0
        wireplumber: Upgrade 0.4.14 -> 0.4.15
        pipewire: Update 0.3.81 -> 0.3.83
        gnome-software: Update 45.0 -> 45.1
        gnome-calendar: Update 45.0 -> 45.1
        gnome-disk-utility: Update 44.0 -> 45.0
        gnome-control-center: Update 45.0 -> 45.1
        eog: Update 45.0 -> 45.1
        gnome-remote-desktop: Update 45.0 -> 45.1
        gnome-shell: Add missing dependency on pipewire
        gnome-shell: Remove deprecated libcroco dependency
        openbox: Drop deprecated libcroco dependency
        pipewire: Update 0.3.83 -> 0.3.84
        tracker-miners: Upgrade 3.6.0 -> 3.6.2
        libgweather4: Upgrade 4.2.0 -> 4.4.0
        gtksourceview5: Upgrade 5.7.1 -> 5.10.0
        openal-soft: Upgrade 1.20.1 -> 1.23.1
        gnome-shell: Upgrade 45.0 -> 45.1
        mutter: Upgrade 45.0 -> 45.1
        dconf-editor: Upgrade 43 -> 45.0.1
        libgsf: Upgrade 1.14.50 -> 1.14.51
        xdg-desktop-portal: Upgrade 1.18.0 -> 1.18.1
        xdg-desktop-portal-gtk: Upgrade 1.14.1 -> 1.15.1
        rest: Upgrade 0.9.0 -> 0.9.1
        nv-codec-headers: Upgrade 12.0.16.0 -> 12.1.14.0
        webp-pixbuf-loader: Upgrade 0.2.4 -> 0.2.5
        libchamplain: Upgrade 0.12.20 -> 0.12.21
        rest: Add packageconfigs for examples and tests
        gssdp: Fix build with api-documentation enabled
        gupnp: Upgrade 0.10.2 -> 0.12.1
        Gupnp-tools upgrade 0.10.2 -> 0.12.1
        gupnp-idg: Upgrade 1.2.0 -> 1.6.0
        gssdp: Upgrade 1.4.0.1 -> 1.6.3
        ghex: Upgrade 3.18.4 -> 45.0
        Adjust vala build according to changes in vala.bbclass
        drop GNOMEBASEBUILDCLASS = "meson"
        gnome-shell-extensions: Upgrade 44.1 -> 45.1
        cups-filters: Fix for current gcc
        gnome-console: Add recipe
        vte9: Fix build with api-documentation enabled
        gnome-terminal: Upgrade 3.48.1 -> 3.50.1
        cups-filters: Upgrade 1.28.17 -> 2.0.0
        gnome-terminal: Remove recommendation on vte-prompt
        ghex: backport patch to fix build for clang
        qpdf: cleanup
        gtksourceview4: Upgrade 4.8.2 -> 4.8.4
        gnome-control-center: Add rdepends
        system-config-printer: Add cups to rdepends
        pipewire: Upgrade 0.3.84 -> 0.3.85
        flatpak: Upgrade 1.15.4 -> 1.15.6
        flatpak: Add packageconfigs for man and docbook docs
        musicpd: unbreak build with ffmpeg 6.1

  Martin Jansa (12):
        nodejs: update to latest v20 version 20.8.1
        nodejs: Revert io_uring support from bundled libuv-1.46.0
        opencv: refresh protobuf-v22 compatibility patch with backported version
        leveldb: prevent installing gtest
        android-tools: drop ${PE}, ${PR} from /usr/src/debug paths
        minifi-cpp: drop ${PE}, ${PR} from /usr/src/debug paths
        xmlrcp-c: drop ${PE}, ${PR} from /usr/src/debug paths
        fluentbit: drop ${PE}, ${PR} from /usr/src/debug paths
        ntpsec, net-snmp: drop ${PE}, ${PR} from /usr/src/debug paths
        aom, x265: drop ${PE}, ${PR} from /usr/src/debug paths
        python3-{h5py,pandas}: drop ${PE}, ${PR} from /usr/src/debug paths
        evince, gnome-calendar, tracker: drop ${PE}, ${PR} from /usr/src/debug paths

  Martin Maurer (1):
        libqmi: Upgrade 1.32.4 -> 1.34.0

  Matthias Klein (1):
        paho-mqtt-c: upgrade 1.3.12 -> 1.3.13

  Mingli Yu (3):
        vboxguestdrivers: Remove the buildpath
        nlohmann-json: Add ptest support
        ptest-packagelists-meta-oe.inc: Add nlohmann-json

  Peter Kjellerstedt (18):
        libwebsockets: Support building for native
        mosquitto: Support building for native again
        jack: Revert to 1.9.22
        pahole: Correct the version in the recipe file name
        neatvnc: Specify the version in the recipe file name
        mstpd: Update to 0.1.0+
        Revert "libtorrent: upgrade 0.13.8 -> 1"
        libtorrent: Add UPSTREAM_CHECK_GITTAGREGEX
        mksh: Update to 59c properly
        fluidsynth: Specify the version in the recipe file name
        libuvc: Specify the version in the recipe file name
        gst-shark: Update to 0.8.1 properly
        xarchiver: Specify the version in the recipe file name
        python3-kivy: Move a comment so it makes more sense
        python3-greenlet: Avoid duplicate URI in SRC_URI
        python3-pylint: Only set SRCREV once
        python3-pytest-mock: Only set SRCREV once
        zeromq: Update to 4.3.5

  Peter Marko (1):
        grpc: Upgrade 1.56.2 -> 1.59.2

  Petr Gotthard (2):
        libmbim: upgrade 1.28.4 -> 1.30.0
        modemmanager: upgrade 1.20.6 -> 1.22.0

  Poonam Jadhav (1):
        sdbus-c++: Update ptest path

  Potin Lai (2):
        libplist: Upgrade to latest master
        idevicerestore: Upgrade to latest master

  Richard Purdie (4):
        meta-python: Drop broken BBCLASSEXTEND variants
        meta-oe: Drop broken BBCLASSEXTEND variants
        meta-networking: Drop broken BBCLASSEXTEND variants
        meta-perl: Drop broken BBCLASSEXTEND variants

  Ross Burton (1):
        yajl: fix CVE-2017-16516, CVE-2022-24795, CVE-2023-33460

  Sam Van Den Berge (1):
        netdata: Upgrade 1.36.1 -> 1.43.0

  Samuli Piippo (2):
        abseil-cpp: fix mingw build
        protobuf: stage protoc binary to sysroot

  Thomas Gessler (1):
        influxdb: Add start script used by systemd service

  Tim Orling (2):
        po4a: remove old recipe
        debsums: remove old recipe

  Trevor Gamblin (5):
        python-git-pw: add from meta-patchtest
        python3-py-cpuinfo: disable broken ptests
        python3-arrow: add from meta-patchtest
        python3-pytest-mock: disable broken ptests
        meta-python: update ptests status for py-cpuinfo, pytest-mock

  Wang Mingyu (149):
        dnf-plugin-tui: create symlinks from /usr/ to /.
        c-ares: upgrade 1.19.1 -> 1.20.1
        adw-gtk3: upgrade 4.9 -> 5.1
        ctags: upgrade 6.0.20230917.0 -> 6.0.20231001.0
        dialog: upgrade 1.3-20230209 -> 1.3-20231002
        freerdp: upgrade 2.11.1 -> 2.11.2
        gnome-backgrounds: upgrade 44.0 -> 45.0
        gnome-calculator: upgrade 45.0 -> 45.0.2
        gnome-font-viewer: upgrade 44.0 -> 45.0
        ipc-run: upgrade 20220807.0 -> 20231003.0
        libbytesize: upgrade 2.9 -> 2.10
        libcoap: upgrade 4.3.3 -> 4.3.4
        libyang: upgrade 2.1.111 -> 2.1.128
        lvgl: upgrade 8.3.9 -> 8.3.10
        metacity: upgrade 3.46.1 -> 3.50.0
        nautilus: upgrade 45.0 -> 45.1
        ceres-solver: upgrade 2.1.0 -> 2.2.0
        python3-eth-abi: upgrade 3.0.1 -> 4.2.1
        python3-mypy: upgrade 1.5.1 -> 1.6.1
        python3-pylint: upgrade 3.0.0 -> 3.0.1
        python3-aiodns: upgrade 3.0.0 -> 3.1.1
        python3-aiohttp: upgrade 3.8.5 -> 3.8.6
        python3-astroid: upgrade 3.0.0 -> 3.0.1
        python3-bitarray: upgrade 2.8.1 -> 2.8.2
        python3-bitstruct: upgrade 8.17.0 -> 8.18.0
        python3-blinker: upgrade 1.6.2 -> 1.6.3
        python3-charset-normalizer: upgrade 3.2.0 -> 3.3.0
        python3-cmake: upgrade 3.27.5 -> 3.27.7
        python3-coverage: upgrade 7.3.1 -> 7.3.2
        python3-croniter: upgrade 1.4.1 -> 2.0.1
        python3-dbus-fast: upgrade 1.85.0 -> 2.12.0
        python3-email-validator: upgrade 1.3.1 -> 2.0.0
        python3-engineio: upgrade 4.7.1 -> 4.8.0
        python3-eth-typing: upgrade 3.4.0 -> 3.5.0
        python3-eth-utils: upgrade 2.2.1 -> 2.2.2
        python3-executing: upgrade 1.2.0 -> 2.0.0
        python3-flask-babel: upgrade 3.1.0 -> 4.0.0
        python3-flask-jwt-extended: upgrade 4.5.2 -> 4.5.3
        python3-google-api-python-client: upgrade 2.101.0 -> 2.104.0
        python3-googleapis-common-protos: upgrade 1.60.0 -> 1.61.0
        python3-google-auth: upgrade 2.23.1 -> 2.23.3
        python3-h5py: upgrade 3.9.0 -> 3.10.0
        python3-huey: upgrade 2.4.5 -> 2.5.0
        python3-imageio: upgrade 2.31.3 -> 2.31.5
        python3-ipython: upgrade 8.15.0 -> 8.16.1
        python3-jedi: upgrade 0.19.0 -> 0.19.1
        python3-meson-python: upgrade 0.13.1 -> 0.14.0
        python3-msgpack: upgrade 1.0.6 -> 1.0.7
        python3-platformdirs: upgrade 3.10.0 -> 3.11.0
        python3-prompt-toolkit: upgrade 3.0.36 -> 3.0.39
        python3-protobuf: upgrade 4.24.3 -> 4.24.4
        python3-pycares: upgrade 4.3.0 -> 4.4.0
        python3-pycodestyle: upgrade 2.11.0 -> 2.11.1
        python3-pydantic: upgrade 2.4.1 -> 2.4.2
        python3-pyephem: upgrade 4.1.4 -> 4.1.5
        python3-pytest-timeout: upgrade 2.1.0 -> 2.2.0
        python3-rapidjson: upgrade 1.11 -> 1.12
        python3-regex: upgrade 2023.8.8 -> 2023.10.3
        python3-rich: upgrade 13.5.3 -> 13.6.0
        python3-schedule: upgrade 1.2.0 -> 1.2.1
        python3-semver: upgrade 3.0.1 -> 3.0.2
        python3-simplejson: upgrade 3.19.1 -> 3.19.2
        python3-socketio: upgrade 5.9.0 -> 5.10.0
        python3-sqlalchemy: upgrade 2.0.21 -> 2.0.22
        python3-stack-data: upgrade 0.6.2 -> 0.6.3
        python3-texttable: upgrade 1.6.7 -> 1.7.0
        python3-traitlets: upgrade 5.10.1 -> 5.11.2
        python3-types-psutil: upgrade 5.9.5.16 -> 5.9.5.17
        python3-tzlocal: upgrade 5.0.1 -> 5.1
        python3-web3: upgrade 6.10.0 -> 6.11.1
        python3-websocket-client: upgrade 1.6.3 -> 1.6.4
        python3-xlsxwriter: upgrade 3.1.3 -> 3.1.8
        python3-xxhash: upgrade 3.3.0 -> 3.4.1
        python3-zeroconf: upgrade 0.112.0 -> 0.119.0
        python3-zopeinterface: upgrade 6.0 -> 6.1
        rdma-core: upgrade 47.0 -> 48.0
        redis: upgrade 7.2.1 -> 7.2.2
        remmina: upgrade 1.4.32 -> 1.4.33
        tesseract: upgrade 5.3.2 -> 5.3.3
        thingsboard-gateway: upgrade 3.3 -> 3.4.1
        tio: upgrade 2.6 -> 2.7
        wireshark: upgrade 4.0.8 -> 4.0.10
        xterm: upgrade 384 -> 387
        zchunk: upgrade 1.3.1 -> 1.3.2
        hdf5: Fix install conflict when enable multilib.
        dnf-plugin-tui: Recover BBCLASSEXTEND variants
        gensio: upgrade 2.7.6 -> 2.7.7
        hwdata: upgrade 0.375 -> 0.376
        libio-socket-ssl-perl: upgrade 2.083 -> 2.084
        makedumpfile: upgrade 1.7.3 -> 1.7.4
        gnome-remote-desktop: move from meta-virtualization to meta-security
        ctags: upgrade 6.0.20231029.0 -> 6.0.20231105.0
        function2: upgrade 4.2.3 -> 4.2.4
        neatvnc: upgrade 0.7.0 -> 0.7.1
        python3-argh: upgrade 0.30.3 -> 0.30.4
        python3-geojson: upgrade 3.0.1 -> 3.1.0
        python3-imageio: upgrade 2.31.6 -> 2.32.0
        python3-inflate64: upgrade 0.3.1 -> 1.0.0
        python3-jsbeautifier: upgrade 1.14.9 -> 1.14.11
        python3-lru-dict: upgrade 1.2.0 -> 1.3.0
        python3-python-vlc: upgrade 3.0.18122 -> 3.0.20123
        python3-zeroconf: upgrade 0.119.0 -> 0.120.0
        c-ares: upgrade 1.21.0 -> 1.22.0
        ctags: upgrade 6.0.20231105.0 -> 6.0.20231112.0
        libencode-perl: upgrade 3.19 -> 3.20
        bindfs: upgrade 1.17.5 -> 1.17.6
        python3-hexbytes: upgrade 0.3.1 -> 1.0.0
        python3-linux-procfs: upgrade 0.7.1 -> 0.7.3
        openvpn: upgrade 2.6.6 -> 2.6.7
        python3-argcomplete: upgrade 3.1.4 -> 3.1.6
        python3-awesomeversion: upgrade 23.8.0 -> 23.11.0
        python3-dbus-fast: upgrade 2.12.0 -> 2.14.0
        python3-eth-typing: upgrade 3.5.1 -> 3.5.2
        python3-eth-utils: upgrade 2.3.0 -> 2.3.1
        python3-geomet: upgrade 1.0.0 -> 1.1.0
        python3-google-api-core: upgrade 2.12.0 -> 2.14.0
        python3-google-api-python-client: upgrade 2.106.0 -> 2.108.0
        python3-mypy: upgrade 1.6.1 -> 1.7.0
        python3-platformdirs: upgrade 3.11.0 -> 4.0.0
        python3-prompt-toolkit: upgrade 3.0.39 -> 3.0.41
        python3-pyaudio: upgrade 0.2.13 -> 0.2.14
        python3-pydantic: upgrade 2.4.2 -> 2.5.0
        python3-pymetno: upgrade 0.11.0 -> 0.12.0
        python3-pytest-xdist: upgrade 3.3.1 -> 3.4.0
        python3-sentry-sdk: upgrade 1.34.0 -> 1.35.0
        python3-tomlkit: upgrade 0.12.2 -> 0.12.3
        python3-types-setuptools: upgrade 68.2.0.0 -> 68.2.0.1
        python3-web3: upgrade 6.11.2 -> 6.11.3
        python3-zeroconf: upgrade 0.126.0 -> 0.127.0
        ser2net: upgrade 4.5.0 -> 4.5.1
        uftp: upgrade 5.0.1 -> 5.0.2
        webkitgtk3: upgrade 2.42.1 -> 2.42.2
        imlib2: delete non-existent file
        c-ares: upgrade 1.22.0 -> 1.22.1
        ctags: upgrade 6.0.20231112.0 -> 6.0.20231119.0
        exiftool: upgrade 12.69 -> 12.70
        gnome-bluetooth: upgrade 42.6 -> 42.7
        libextutils-cppguess-perl: upgrade 0.26 -> 0.27
        libwebsockets: upgrade 4.3.2 -> 4.3.3
        python3-aiohttp: upgrade 3.8.6 -> 3.9.0
        python3-dateparser: upgrade 1.1.8 -> 1.2.0
        python3-django: upgrade 4.2.5 -> 4.2.7
        python3-imageio: upgrade 2.32.0 -> 2.33.0
        python3-ldap: upgrade 3.4.3 -> 3.4.4
        python3-pastedeploy: upgrade 3.0.1 -> 3.1.0
        python3-pdm: upgrade 2.10.1 -> 2.10.3
        python3-pydantic: upgrade 2.5.0 -> 2.5.1
        python3-rich: upgrade 13.6.0 -> 13.7.0
        strongswan: upgrade 5.9.11 -> 5.9.12

  Yi Zhao (6):
        samba: upgrade 4.18.6 -> 4.18.8
        samba: use external cmocka instead of bundled cmocka
        libtevent: fix ptest
        libldb: add ptest
        conntrack-tools: upgrade 1.4.7 -> 1.4.8
        nftables: upgrade 1.0.8 -> 1.0.9

  Yoann Congal (5):
        emlog: ignore CVE-2022-3968 & CVE-2023-43291
        juce/projucer: Backport a fix for the compilation under recent GCC
        meta-oe/static-ids: Change postgres to 28 to match forced id in recipe
        static-id: add missing netdata group
        python3-soupsieve: Break circular dependency with beautifulsoup4

  Zoltán Böszörményi (3):
        python3-ninja-syntax: Set BBCLASSEXTEND = "native nativesdk"
        python3-ninja: Set BBCLASSEXTEND = "native nativesdk"
        geos: Fix packaging

  alperak (39):
        xdebug: upgrade 3.2.0 -> 3.2.2
        catch2: upgrade 2.13.7 -> 2.13.10
        tuna: upgrade 0.18 -> 0.19
        libsrtp: upgrade 2.4.2 -> 2.5.0
        libupnp: upgrade 1.14.6 -> 1.14.18
        libisofs: upgrade 1.5.4 -> 1.5.6
        libisoburn: 1.5.4 -> 1.5.6
        fuse-exfat: upgrade 1.3.0 -> 1.4.0
        fuse3: upgrade 3.15.1 -> 3.16.2
        ufs-utils: upgrade 3.12.3 -> 4.13.5
        libebml: upgrade 1.3.0 -> 1.4.4
        libmatroska: upgrade 1.4.1 -> 1.7.1
        libde265: upgrade 1.0.5 -> 1.0.12
        libopenmpt: upgrade 0.6.2 -> 0.7.3
        mpd: upgrade 0.23.12 -> 0.23.14
        opencore-amr: upgrade 0.1.3 -> 0.1.6
        tinyalsa: upgrade 1.1.1 -> 2.0.0
        cannelloni: upgrade 1.0.0 -> 1.1.0
        civetweb: upgrade 1.12 -> 1.16
        libdnet: upgrade 1.16.3 -> 1.17.0
        openfortivpn: upgrade 1.20.5 -> 1.21.0
        fuse-exfat: Dropped md5sum
        libopenmpt: Added license change reason and dropped md5sum
        bolt: upgrade 0.9.5 -> 0.9.6
        irssi: upgrade 1.4.4 -> 1.4.5
        libmtp: upgrade 1.1.20 -> 1.1.21
        libsigc++-2.0: upgrade 2.10.7 -> 2.12.1
        libsigc++-3: upgrade 3.2.0 -> 3.6.0
        ocl-icd: upgrade 2.3.1 -> 2.3.2
        opencl-icd-loader: upgrade v2022.01.04 -> v2023.04.17
        uutils-coreutils: upgrade 0.0.22 -> 0.0.23
        botan: upgrade 2.19.3 -> 3.2.0
        capnproto: upgrade 0.10.4 -> 1.0.1
        cloc: upgrade 1.94 -> 1.98
        cpuid: upgrade 20211129 -> 20230614
        gst-editing-services: upgrade 1.20.5 -> 1.22.7
        luaposix: upgrade 35.1 -> 36.2.1
        mercurial: upgrade 6.1 -> 6.5
        ledmon: upgrade 0.93 -> 0.97

  skandigraun (1):
        libvpx: don't specify armv5 and armv6 toolchains explicitly

meta-security: 3f7d40b0fc..070a1e82cc:
  Gowtham Suresh Kumar (1):
        Update parsec recipes

  Mingli Yu (1):
        samhain: remove the buildpath

  Stefan Berger (1):
        ima,evm: Add two variables to write filenames and signatures into

Change-Id: Ib809aa0df4162c50a06c542a94a0b06cdc149a2d
Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
diff --git a/poky/meta/lib/bblayers/setupwriters/oe-setup-layers.py b/poky/meta/lib/bblayers/setupwriters/oe-setup-layers.py
index d5bc19a..bd71ca1 100644
--- a/poky/meta/lib/bblayers/setupwriters/oe-setup-layers.py
+++ b/poky/meta/lib/bblayers/setupwriters/oe-setup-layers.py
@@ -45,7 +45,7 @@
         logger.info('Created {}.json'.format(output))
         if not args.json_only:
             self._write_python(os.path.join(os.path.dirname(__file__),'../../../../scripts/oe-setup-layers'), output)
-        logger.info('Created {}'.format(output))
+            logger.info('Created {}'.format(output))
 
     def register_arguments(self, parser):
         parser.add_argument('--json-only', action='store_true',
diff --git a/poky/meta/lib/oe/__init__.py b/poky/meta/lib/oe/__init__.py
index da7cbab..6eb536a 100644
--- a/poky/meta/lib/oe/__init__.py
+++ b/poky/meta/lib/oe/__init__.py
@@ -9,4 +9,4 @@
 
 BBIMPORTS = ["data", "path", "utils", "types", "package", "packagedata", \
              "packagegroup", "sstatesig", "lsb", "cachedpath", "license", \
-             "qa", "reproducible", "rust", "buildcfg"]
+             "qa", "reproducible", "rust", "buildcfg", "go"]
diff --git a/poky/meta/lib/oe/buildcfg.py b/poky/meta/lib/oe/buildcfg.py
index 90f5e05..b3fe510 100644
--- a/poky/meta/lib/oe/buildcfg.py
+++ b/poky/meta/lib/oe/buildcfg.py
@@ -1,26 +1,27 @@
 
+import os
 import subprocess
 import bb.process
 
 def detect_revision(d):
     path = get_scmbasepath(d)
-    return get_metadata_git_revision(path, d)
+    return get_metadata_git_revision(path)
 
 def detect_branch(d):
     path = get_scmbasepath(d)
-    return get_metadata_git_branch(path, d)
+    return get_metadata_git_branch(path)
 
 def get_scmbasepath(d):
     return os.path.join(d.getVar('COREBASE'), 'meta')
 
-def get_metadata_git_branch(path, d):
+def get_metadata_git_branch(path):
     try:
         rev, _ = bb.process.run('git rev-parse --abbrev-ref HEAD', cwd=path)
     except bb.process.ExecutionError:
         rev = '<unknown>'
     return rev.strip()
 
-def get_metadata_git_revision(path, d):
+def get_metadata_git_revision(path):
     try:
         rev, _ = bb.process.run('git rev-parse HEAD', cwd=path)
     except bb.process.ExecutionError:
@@ -45,5 +46,5 @@
     layers = (d.getVar("BBLAYERS") or "").split()
     revisions = []
     for i in layers:
-        revisions.append((i, os.path.basename(i), get_metadata_git_branch(i, None).strip(), get_metadata_git_revision(i, None), is_layer_modified(i)))
+        revisions.append((i, os.path.basename(i), get_metadata_git_branch(i).strip(), get_metadata_git_revision(i), is_layer_modified(i)))
     return revisions
diff --git a/poky/meta/lib/oe/copy_buildsystem.py b/poky/meta/lib/oe/copy_buildsystem.py
index a0d8290..81abfbf 100644
--- a/poky/meta/lib/oe/copy_buildsystem.py
+++ b/poky/meta/lib/oe/copy_buildsystem.py
@@ -22,7 +22,7 @@
     mode = os.stat(src).st_mode
     if stat.S_ISDIR(mode):
         bb.utils.mkdirhier(dest)
-        cmd = "tar --exclude='.git' --exclude='__pycache__' --xattrs --xattrs-include='*' -chf - -C %s -p . \
+        cmd = "tar --exclude='.git' --exclude='__pycache__' --xattrs --xattrs-include='*' -cf - -C %s -p . \
         | tar --xattrs --xattrs-include='*' -xf - -C %s" % (src, dest)
         subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
     else:
diff --git a/poky/meta/lib/oe/cve_check.py b/poky/meta/lib/oe/cve_check.py
index 3979d52..3fa77bf 100644
--- a/poky/meta/lib/oe/cve_check.py
+++ b/poky/meta/lib/oe/cve_check.py
@@ -95,11 +95,6 @@
     for url in oe.patch.src_patches(d):
         patch_file = bb.fetch.decodeurl(url)[2]
 
-        # Remote compressed patches may not be unpacked, so silently ignore them
-        if not os.path.isfile(patch_file):
-            bb.warn("%s does not exist, cannot extract CVE list" % patch_file)
-            continue
-
         # Check patch file name for CVE ID
         fname_match = cve_file_name_match.search(patch_file)
         if fname_match:
@@ -107,6 +102,12 @@
             patched_cves.add(cve)
             bb.debug(2, "Found CVE %s from patch file name %s" % (cve, patch_file))
 
+        # Remote patches won't be present and compressed patches won't be
+        # unpacked, so say we're not scanning them
+        if not os.path.isfile(patch_file):
+            bb.note("%s is remote or compressed, not scanning content" % patch_file)
+            continue
+
         with open(patch_file, "r", encoding="utf-8") as f:
             try:
                 patch_text = f.read()
@@ -172,7 +173,7 @@
 
     for product in output["package"]:
         if product["name"] == data["package"][0]["name"]:
-            bb.error("Error adding the same package twice")
+            bb.error("Error adding the same package %s twice" % product["name"])
             return
 
     output["package"].append(data["package"][0])
diff --git a/poky/meta/lib/oe/go.py b/poky/meta/lib/oe/go.py
new file mode 100644
index 0000000..dfd957d
--- /dev/null
+++ b/poky/meta/lib/oe/go.py
@@ -0,0 +1,34 @@
+#
+# Copyright OpenEmbedded Contributors
+#
+# SPDX-License-Identifier: MIT
+#
+
+import re
+
+def map_arch(a):
+    if re.match('i.86', a):
+        return '386'
+    elif a == 'x86_64':
+        return 'amd64'
+    elif re.match('arm.*', a):
+        return 'arm'
+    elif re.match('aarch64.*', a):
+        return 'arm64'
+    elif re.match('mips64el.*', a):
+        return 'mips64le'
+    elif re.match('mips64.*', a):
+        return 'mips64'
+    elif a == 'mips':
+        return 'mips'
+    elif a == 'mipsel':
+        return 'mipsle'
+    elif re.match('p(pc|owerpc)(64le)', a):
+        return 'ppc64le'
+    elif re.match('p(pc|owerpc)(64)', a):
+        return 'ppc64'
+    elif a == 'riscv64':
+        return 'riscv64'
+    elif a == 'loongarch64':
+        return 'loong64'
+    return ''
diff --git a/poky/meta/lib/oe/package.py b/poky/meta/lib/oe/package.py
index 1dd20f8..f69bf9c 100644
--- a/poky/meta/lib/oe/package.py
+++ b/poky/meta/lib/oe/package.py
@@ -456,8 +456,7 @@
                 'sbindir',
                 'libexecdir',
                 'libdir',
-                'includedir',
-                'oldincludedir' ]
+                'includedir' ]
 
     for path in target_path_vars:
         dir = d.getVar(path) or ""
diff --git a/poky/meta/lib/oe/patch.py b/poky/meta/lib/oe/patch.py
index ff9afc9..9b480b2 100644
--- a/poky/meta/lib/oe/patch.py
+++ b/poky/meta/lib/oe/patch.py
@@ -772,8 +772,9 @@
             self.patchset.Push()
         except Exception:
             import sys
-            os.chdir(olddir)
             raise
+        finally:
+            os.chdir(olddir)
 
 # Patch resolver which relies on the user doing all the work involved in the
 # resolution, with the exception of refreshing the remote copy of the patch
@@ -833,9 +834,9 @@
                             # User did not fix the problem.  Abort.
                             raise PatchError("Patch application failed, and user did not fix and refresh the patch.")
         except Exception:
-            os.chdir(olddir)
             raise
-        os.chdir(olddir)
+        finally:
+            os.chdir(olddir)
 
 
 def patch_path(url, fetch, workdir, expand=True):
diff --git a/poky/meta/lib/oe/path.py b/poky/meta/lib/oe/path.py
index 0dc8f17..e2f1913 100644
--- a/poky/meta/lib/oe/path.py
+++ b/poky/meta/lib/oe/path.py
@@ -125,7 +125,8 @@
         if os.path.isdir(src):
             if len(glob.glob('%s/.??*' % src)) > 0:
                 source = './.??* '
-            source += './*'
+            if len(glob.glob('%s/**' % src)) > 0:
+                source += './*'
             s_dir = src
         else:
             source = src
diff --git a/poky/meta/lib/oe/sstatesig.py b/poky/meta/lib/oe/sstatesig.py
index 5bf1697..e250f51 100644
--- a/poky/meta/lib/oe/sstatesig.py
+++ b/poky/meta/lib/oe/sstatesig.py
@@ -142,9 +142,10 @@
         super().set_taskdata(data[3:])
 
     def dump_sigs(self, dataCache, options):
-        sigfile = os.getcwd() + "/locked-sigs.inc"
-        bb.plain("Writing locked sigs to %s" % sigfile)
-        self.dump_lockedsigs(sigfile)
+        if 'lockedsigs' in options:
+            sigfile = os.getcwd() + "/locked-sigs.inc"
+            bb.plain("Writing locked sigs to %s" % sigfile)
+            self.dump_lockedsigs(sigfile)
         return super(bb.siggen.SignatureGeneratorBasicHash, self).dump_sigs(dataCache, options)
 
 
@@ -598,9 +599,9 @@
                         update_hash(" %10s" % pwd.getpwuid(s.st_uid).pw_name)
                         update_hash(" %10s" % grp.getgrgid(s.st_gid).gr_name)
                     except KeyError as e:
-                        bb.warn("KeyError in %s" % path)
                         msg = ("KeyError: %s\nPath %s is owned by uid %d, gid %d, which doesn't match "
-                            "any user/group on target. This may be due to host contamination." % (e, path, s.st_uid, s.st_gid))
+                            "any user/group on target. This may be due to host contamination." %
+                            (e, os.path.abspath(path), s.st_uid, s.st_gid))
                         raise Exception(msg).with_traceback(e.__traceback__)
 
                 if include_timestamps:
diff --git a/poky/meta/lib/oe/utils.py b/poky/meta/lib/oe/utils.py
index a3b1bb1..14a7d07 100644
--- a/poky/meta/lib/oe/utils.py
+++ b/poky/meta/lib/oe/utils.py
@@ -7,6 +7,7 @@
 import subprocess
 import multiprocessing
 import traceback
+import errno
 
 def read_file(filename):
     try:
@@ -528,3 +529,14 @@
         total += sum(roundup(getsize(os.path.join(root, name))) for name in files)
         total += roundup(getsize(root))
     return total
+
+# Update the mtime of a file, skip if permission/read-only issues
+def touch(filename):
+    try:
+        os.utime(filename, None)
+    except PermissionError:
+        pass
+    except OSError as e:
+        # Handle read-only file systems gracefully
+        if e.errno != errno.EROFS:
+            raise e
diff --git a/poky/meta/lib/oeqa/core/target/ssh.py b/poky/meta/lib/oeqa/core/target/ssh.py
index f22836d..f4dd0ca 100644
--- a/poky/meta/lib/oeqa/core/target/ssh.py
+++ b/poky/meta/lib/oeqa/core/target/ssh.py
@@ -232,11 +232,12 @@
         output_raw = b''
         starttime = time.time()
         process = subprocess.Popen(command, **options)
+        has_timeout = False
         if timeout:
             endtime = starttime + timeout
             eof = False
             os.set_blocking(process.stdout.fileno(), False)
-            while time.time() < endtime and not eof:
+            while not has_timeout and not eof:
                 try:
                     logger.debug('Waiting for process output: time: %s, endtime: %s' % (time.time(), endtime))
                     if select.select([process.stdout], [], [], 5)[0] != []:
@@ -257,6 +258,10 @@
                     logger.debug('BlockingIOError')
                     continue
 
+                if time.time() >= endtime:
+                    logger.debug('SSHCall has timeout! Time: %s, endtime: %s' % (time.time(), endtime))
+                    has_timeout = True
+
             process.stdout.close()
 
             # process hasn't returned yet
@@ -293,6 +298,16 @@
                     pass
                 process.wait()
 
+        if has_timeout:
+            # Version of openssh before 8.6_p1 returns error code 0 when killed
+            # by a signal, when the timeout occurs we will receive a 0 error
+            # code because the process is been terminated and it's wrong because
+            # that value means success, but the process timed out.
+            # Afterwards, from version 8.6_p1 onwards, the returned code is 255.
+            # Fix this behaviour by checking the return code
+            if process.returncode == 0:
+                process.returncode = 255
+
     options = {
         "stdout": subprocess.PIPE,
         "stderr": subprocess.STDOUT,
diff --git a/poky/meta/lib/oeqa/runtime/cases/parselogs.py b/poky/meta/lib/oeqa/runtime/cases/parselogs.py
index a805edd..cddb846 100644
--- a/poky/meta/lib/oeqa/runtime/cases/parselogs.py
+++ b/poky/meta/lib/oeqa/runtime/cases/parselogs.py
@@ -180,12 +180,6 @@
         'Failed to load DMC firmware',
         'The driver is built-in, so to load the firmware you need to',
         ] + x86_common,
-    'edgerouter' : [
-        'not creating \'/sys/firmware/fdt\'',
-        'Failed to find cpu0 device node',
-        'Fatal server error:',
-        'Server terminated with error',
-        ] + common_errors,
     'beaglebone-yocto' : [
         'Direct firmware load for regulatory.db',
         'failed to load regulatory.db',
diff --git a/poky/meta/lib/oeqa/runtime/cases/ssh.py b/poky/meta/lib/oeqa/runtime/cases/ssh.py
index 13aac54..cdbef59 100644
--- a/poky/meta/lib/oeqa/runtime/cases/ssh.py
+++ b/poky/meta/lib/oeqa/runtime/cases/ssh.py
@@ -13,6 +13,9 @@
     @OETestDepends(['ping.PingTest.test_ping'])
     @OEHasPackage(['dropbear', 'openssh-sshd'])
     def test_ssh(self):
+        (status, output) = self.target.run('sleep 20', timeout=2)
+        msg='run() timed out but return code was zero.'
+        self.assertNotEqual(status, 0, msg=msg)
         (status, output) = self.target.run('uname -a')
         self.assertEqual(status, 0, msg='SSH Test failed: %s' % output)
         (status, output) = self.target.run('cat /etc/controllerimage')
diff --git a/poky/meta/lib/oeqa/sdk/cases/rust.py b/poky/meta/lib/oeqa/sdk/cases/rust.py
index 31036f0..f5d437b 100644
--- a/poky/meta/lib/oeqa/sdk/cases/rust.py
+++ b/poky/meta/lib/oeqa/sdk/cases/rust.py
@@ -33,3 +33,25 @@
 
     def test_cargo_build(self):
         self._run('cd %s/hello; cargo build' % self.tc.sdk_dir)
+
+class RustHostCompileTest(OESDKTestCase):
+    td_vars = ['MACHINE', 'SDK_SYS']
+
+    @classmethod
+    def setUpClass(self):
+        targetdir = os.path.join(self.tc.sdk_dir, "hello")
+        try:
+            shutil.rmtree(targetdir)
+        except FileNotFoundError:
+            pass
+        shutil.copytree(os.path.join(self.tc.sdk_files_dir, "rust/hello"), targetdir)
+
+    def setUp(self):
+        machine = self.td.get("MACHINE")
+        if not self.tc.hasHostPackage("packagegroup-rust-cross-canadian-%s" % machine):
+            raise unittest.SkipTest("RustCompileTest class: SDK doesn't contain a Rust cross-canadian toolchain")
+
+    def test_cargo_build(self):
+        sdksys = self.td.get("SDK_SYS")
+        self._run('cd %s/hello; cargo build --target %s-gnu' % (self.tc.sdk_dir, sdksys))
+        self._run('cd %s/hello; cargo run --target %s-gnu' % (self.tc.sdk_dir, sdksys))
diff --git a/poky/meta/lib/oeqa/selftest/case.py b/poky/meta/lib/oeqa/selftest/case.py
index 54d90c7..da35b25 100644
--- a/poky/meta/lib/oeqa/selftest/case.py
+++ b/poky/meta/lib/oeqa/selftest/case.py
@@ -117,10 +117,6 @@
                 if e.errno != errno.ENOENT:
                     raise
 
-        if self.tc.custommachine:
-            machine_conf = 'MACHINE ??= "%s"\n' % self.tc.custommachine
-            self.set_machine_config(machine_conf)
-
         # tests might need their own setup
         # but if they overwrite this one they have to call
         # super each time, so let's give them an alternative
@@ -178,19 +174,11 @@
         self.logger.debug("Writing to: %s\n%s\n" % (dest_path, data))
         ftools.write_file(dest_path, data)
 
-        if not multiconfig and self.tc.custommachine and 'MACHINE' in data:
-            machine = get_bb_var('MACHINE')
-            self.logger.warning('MACHINE overridden: %s' % machine)
-
     def append_config(self, data):
         """Append to <builddir>/conf/selftest.inc"""
         self.logger.debug("Appending to: %s\n%s\n" % (self.testinc_path, data))
         ftools.append_file(self.testinc_path, data)
 
-        if self.tc.custommachine and 'MACHINE' in data:
-            machine = get_bb_var('MACHINE')
-            self.logger.warning('MACHINE overridden: %s' % machine)
-
     def remove_config(self, data):
         """Remove data from <builddir>/conf/selftest.inc"""
         self.logger.debug("Removing from: %s\n%s\n" % (self.testinc_path, data))
diff --git a/poky/meta/lib/oeqa/selftest/cases/archiver.py b/poky/meta/lib/oeqa/selftest/cases/archiver.py
index 3fa59ff..3cb888c 100644
--- a/poky/meta/lib/oeqa/selftest/cases/archiver.py
+++ b/poky/meta/lib/oeqa/selftest/cases/archiver.py
@@ -141,7 +141,7 @@
         pn = 'gcc-source-%s' % get_bb_vars(['PV'], 'gcc')['PV']
 
         # Generate the tasks signatures
-        bitbake('mc:mc1:%s mc:mc2:%s -c %s -S none' % (pn, pn, task))
+        bitbake('mc:mc1:%s mc:mc2:%s -c %s -S lockedsigs' % (pn, pn, task))
 
         # Check the tasks signatures
         # To be machine agnostic the tasks needs to generate the same signature for each machine
diff --git a/poky/meta/lib/oeqa/selftest/cases/buildoptions.py b/poky/meta/lib/oeqa/selftest/cases/buildoptions.py
index 1044484..31dafaa 100644
--- a/poky/meta/lib/oeqa/selftest/cases/buildoptions.py
+++ b/poky/meta/lib/oeqa/selftest/cases/buildoptions.py
@@ -14,6 +14,7 @@
 from oeqa.core.decorator.data import skipIfMachine
 from oeqa.utils.commands import bitbake, get_bb_var, get_bb_vars
 import oeqa.utils.ftools as ftools
+from oeqa.core.decorator import OETestTag
 
 class ImageOptionsTests(OESelftestTestCase):
 
@@ -204,6 +205,7 @@
         self.write_config(features)
         bitbake('fortran-helloworld')
 
+@OETestTag("yocto-mirrors")
 class SourceMirroring(OESelftestTestCase):
     # Can we download everything from the Yocto Sources Mirror over http only
     def test_yocto_source_mirror(self):
diff --git a/poky/meta/lib/oeqa/selftest/cases/debuginfod.py b/poky/meta/lib/oeqa/selftest/cases/debuginfod.py
index 37f5176..505b4be 100644
--- a/poky/meta/lib/oeqa/selftest/cases/debuginfod.py
+++ b/poky/meta/lib/oeqa/selftest/cases/debuginfod.py
@@ -6,7 +6,11 @@
 import os
 import socketserver
 import subprocess
+import time
+import urllib
+import pathlib
 
+from oeqa.core.decorator import OETestTag
 from oeqa.selftest.case import OESelftestTestCase
 from oeqa.utils.commands import bitbake, get_bb_var, runqemu
 
@@ -21,39 +25,54 @@
         Request the metrics endpoint periodically and wait for there to be no
         busy scanning threads.
 
-        Returns True if debuginfod is ready, False if we timed out
+        Returns if debuginfod is ready, raises an exception if not within the
+        timeout.
         """
-        import time, urllib
 
-        # Wait a minute
-        countdown = 6
-        delay = 10
+        # Wait two minutes
+        countdown = 24
+        delay = 5
+        latest = None
 
         while countdown:
+            self.logger.info("waiting...")
             time.sleep(delay)
+
+            self.logger.info("polling server")
+            if self.debuginfod.poll():
+                self.logger.info("server dead")
+                self.debuginfod.communicate()
+                self.fail("debuginfod terminated unexpectedly")
+            self.logger.info("server alive")
+
             try:
-                with urllib.request.urlopen("http://localhost:%d/metrics" % port) as f:
-                    lines = f.read().decode("ascii").splitlines()
-                    if "thread_busy{role=\"scan\"} 0" in lines:
-                        return True
+                with urllib.request.urlopen("http://localhost:%d/metrics" % port, timeout=10) as f:
+                    for line in f.read().decode("ascii").splitlines():
+                        key, value = line.rsplit(" ", 1)
+                        if key == "thread_busy{role=\"scan\"}":
+                            latest = int(value)
+                            self.logger.info("Waiting for %d scan jobs to finish" % latest)
+                            if latest == 0:
+                                return
             except urllib.error.URLError as e:
+                # TODO: how to catch just timeouts?
                 self.logger.error(e)
+
             countdown -= 1
-        return False
 
+        raise TimeoutError("Cannot connect debuginfod, still %d scan jobs running" % latest)
 
-    def test_debuginfod(self):
-        self.write_config(
-            """
-DISTRO_FEATURES:append = " debuginfod"
-CORE_IMAGE_EXTRA_INSTALL += "elfutils"
-        """
-        )
-        bitbake("core-image-minimal elfutils-native:do_addto_recipe_sysroot")
+    def start_debuginfod(self):
+        # We assume that the caller has already bitbake'd elfutils-native:do_addto_recipe_sysroot
 
-        native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "elfutils-native")
+        # Save some useful paths for later
+        native_sysroot = pathlib.Path(get_bb_var("RECIPE_SYSROOT_NATIVE", "elfutils-native"))
+        native_bindir = native_sysroot / "usr" / "bin"
+        self.debuginfod = native_bindir / "debuginfod"
+        self.debuginfod_find = native_bindir / "debuginfod-find"
+
         cmd = [
-            os.path.join(native_sysroot, "usr", "bin", "debuginfod"),
+            self.debuginfod,
             "--verbose",
             # In-memory database, this is a one-shot test
             "--database=:memory:",
@@ -76,31 +95,64 @@
         else:
             self.fail("Unknown package class %s" % format)
 
-        # Find a free port
+        # Find a free port. Racey but the window is small.
         with socketserver.TCPServer(("localhost", 0), None) as s:
-            port = s.server_address[1]
-            cmd.append("--port=%d" % port)
+            self.port = s.server_address[1]
+            cmd.append("--port=%d" % self.port)
+
+        self.logger.info(f"Starting server {cmd}")
+        self.debuginfod = subprocess.Popen(cmd, env={})
+        self.wait_for_debuginfod(self.port)
+
+
+    def test_debuginfod_native(self):
+        """
+        Test debuginfod outside of qemu, by building a package and looking up a
+        binary's debuginfo using elfutils-native.
+        """
+
+        self.write_config("""
+TMPDIR = "${TOPDIR}/tmp-debuginfod"
+DISTRO_FEATURES:append = " debuginfod"
+""")
+        bitbake("elfutils-native:do_addto_recipe_sysroot xz xz:do_package")
 
         try:
-            # Remove DEBUGINFOD_URLS from the environment so we don't try
-            # looking in the distro debuginfod
-            env = os.environ.copy()
-            if "DEBUGINFOD_URLS" in env:
-                del env["DEBUGINFOD_URLS"]
+            self.start_debuginfod()
 
-            self.logger.info(f"Starting server {cmd}")
-            debuginfod = subprocess.Popen(cmd, env=env)
+            env = os.environ.copy()
+            env["DEBUGINFOD_URLS"] = "http://localhost:%d/" % self.port
+
+            pkgs = pathlib.Path(get_bb_var("PKGDEST", "xz"))
+            cmd = (self.debuginfod_find, "debuginfo", pkgs / "xz" / "usr" / "bin" / "xz.xz")
+            self.logger.info(f"Starting client {cmd}")
+            output = subprocess.check_output(cmd, env=env, text=True)
+            # This should be more comprehensive
+            self.assertIn("/.cache/debuginfod_client/", output)
+        finally:
+            self.debuginfod.kill()
+
+    @OETestTag("runqemu")
+    def test_debuginfod_qemu(self):
+        """
+        Test debuginfod-find inside a qemu, talking to a debuginfod on the host.
+        """
+
+        self.write_config("""
+TMPDIR = "${TOPDIR}/tmp-debuginfod"
+DISTRO_FEATURES:append = " debuginfod"
+CORE_IMAGE_EXTRA_INSTALL += "elfutils xz"
+        """)
+        bitbake("core-image-minimal elfutils-native:do_addto_recipe_sysroot")
+
+        try:
+            self.start_debuginfod()
 
             with runqemu("core-image-minimal", runqemuparams="nographic") as qemu:
-                self.assertTrue(self.wait_for_debuginfod(port))
-
-                cmd = (
-                    "DEBUGINFOD_URLS=http://%s:%d/ debuginfod-find debuginfo /usr/bin/debuginfod"
-                    % (qemu.server_ip, port)
-                )
+                cmd = "DEBUGINFOD_URLS=http://%s:%d/ debuginfod-find debuginfo /usr/bin/xz" % (qemu.server_ip, self.port)
                 self.logger.info(f"Starting client {cmd}")
                 status, output = qemu.run_serial(cmd)
                 # This should be more comprehensive
                 self.assertIn("/.cache/debuginfod_client/", output)
         finally:
-            debuginfod.kill()
+            self.debuginfod.kill()
diff --git a/poky/meta/lib/oeqa/selftest/cases/devtool.py b/poky/meta/lib/oeqa/selftest/cases/devtool.py
index b577f6d..ab58971 100644
--- a/poky/meta/lib/oeqa/selftest/cases/devtool.py
+++ b/poky/meta/lib/oeqa/selftest/cases/devtool.py
@@ -27,6 +27,9 @@
     corecopydir = os.path.join(templayerdir, 'core-copy')
     bblayers_conf = os.path.join(os.environ['BUILDDIR'], 'conf', 'bblayers.conf')
     edited_layers = []
+    # make sure user doesn't have a local workspace
+    result = runCmd('bitbake-layers show-layers')
+    assert "workspacelayer" not in result.output, "Devtool test suite cannot be run with a local workspace directory"
 
     # We need to take a copy of the meta layer so we can modify it and not
     # have any races against other tests that might be running in parallel
@@ -572,7 +575,7 @@
         checkvars['S'] = '${WORKDIR}/MarkupSafe-%s' % testver
         checkvars['SRC_URI'] = url
         self._test_recipe_contents(recipefile, checkvars, [])
-     
+
     def test_devtool_add_fetch_git(self):
         tempdir = tempfile.mkdtemp(prefix='devtoolqa')
         self.track_for_cleanup(tempdir)
@@ -1072,7 +1075,12 @@
             with open(source, "rt") as f:
                 content = f.read()
             self.assertEquals(content, expected)
-        check('devtool', 'This is a test for something\n')
+        if self.td["MACHINE"] == "qemux86":
+            check('devtool', 'This is a test for qemux86\n')
+        elif self.td["MACHINE"] == "qemuarm":
+            check('devtool', 'This is a test for qemuarm\n')
+        else:
+            check('devtool', 'This is a test for something\n')
         check('devtool-no-overrides', 'This is a test for something\n')
         check('devtool-override-qemuarm', 'This is a test for qemuarm\n')
         check('devtool-override-qemux86', 'This is a test for qemux86\n')
diff --git a/poky/meta/lib/oeqa/selftest/cases/recipetool.py b/poky/meta/lib/oeqa/selftest/cases/recipetool.py
index 48661be..55cbba9 100644
--- a/poky/meta/lib/oeqa/selftest/cases/recipetool.py
+++ b/poky/meta/lib/oeqa/selftest/cases/recipetool.py
@@ -445,7 +445,7 @@
         # Basic test to see if github URL mangling works
         temprecipe = os.path.join(self.tempdir, 'recipe')
         os.makedirs(temprecipe)
-        recipefile = os.path.join(temprecipe, 'meson_git.bb')
+        recipefile = os.path.join(temprecipe, 'python3-meson_git.bb')
         srcuri = 'https://github.com/mesonbuild/meson;rev=0.32.0'
         result = runCmd(['recipetool', 'create', '-o', temprecipe, srcuri])
         self.assertTrue(os.path.isfile(recipefile))
@@ -474,12 +474,149 @@
         inherits = ['setuptools3']
         self._test_recipe_contents(recipefile, checkvars, inherits)
 
+    def test_recipetool_create_python3_pep517_setuptools_build_meta(self):
+        # This test require python 3.11 or above for the tomllib module
+        # or tomli module to be installed
+        try:
+            import tomllib
+        except ImportError:
+            try:
+                import tomli
+            except ImportError:
+                self.skipTest('Test requires python 3.11 or above for tomllib module or tomli module')
+
+        # Test creating python3 package from tarball (using setuptools.build_meta class)
+        temprecipe = os.path.join(self.tempdir, 'recipe')
+        os.makedirs(temprecipe)
+        pn = 'webcolors'
+        pv = '1.13'
+        recipefile = os.path.join(temprecipe, 'python3-%s_%s.bb' % (pn, pv))
+        srcuri = 'https://files.pythonhosted.org/packages/a1/fb/f95560c6a5d4469d9c49e24cf1b5d4d21ffab5608251c6020a965fb7791c/%s-%s.tar.gz' % (pn, pv)
+        result = runCmd('recipetool create -o %s %s' % (temprecipe, srcuri))
+        self.assertTrue(os.path.isfile(recipefile))
+        checkvars = {}
+        checkvars['SUMMARY'] = 'A library for working with the color formats defined by HTML and CSS.'
+        checkvars['LICENSE'] = set(['BSD-3-Clause'])
+        checkvars['LIC_FILES_CHKSUM'] = 'file://LICENSE;md5=702b1ef12cf66832a88f24c8f2ee9c19'
+        checkvars['SRC_URI'] = 'https://files.pythonhosted.org/packages/a1/fb/f95560c6a5d4469d9c49e24cf1b5d4d21ffab5608251c6020a965fb7791c/webcolors-${PV}.tar.gz'
+        checkvars['SRC_URI[md5sum]'] = 'c9be30c5b0cf1cad32e4cbacbb2229e9'
+        checkvars['SRC_URI[sha1sum]'] = 'c90b84fb65eed9b4c9dea7f08c657bfac0e820a5'
+        checkvars['SRC_URI[sha256sum]'] = 'c225b674c83fa923be93d235330ce0300373d02885cef23238813b0d5668304a'
+        checkvars['SRC_URI[sha384sum]'] = '45652af349660f19f68d01361dd5bda287789e5ea63608f52a8cea526ac04465614db2ea236103fb8456b1fcaea96ed7'
+        checkvars['SRC_URI[sha512sum]'] = '074aaf135ac6b0025b88b731d1d6dfa4c539b4fff7195658cc58a4326bb9f0449a231685d312b4a1ec48ca535a838bfa5c680787fe0e61473a2a092c448937d0'
+        inherits = ['python_setuptools_build_meta']
+
+        self._test_recipe_contents(recipefile, checkvars, inherits)
+
+    def test_recipetool_create_python3_pep517_poetry_core_masonry_api(self):
+        # This test require python 3.11 or above for the tomllib module
+        # or tomli module to be installed
+        try:
+            import tomllib
+        except ImportError:
+            try:
+                import tomli
+            except ImportError:
+                self.skipTest('Test requires python 3.11 or above for tomllib module or tomli module')
+
+        # Test creating python3 package from tarball (using poetry.core.masonry.api class)
+        temprecipe = os.path.join(self.tempdir, 'recipe')
+        os.makedirs(temprecipe)
+        pn = 'iso8601'
+        pv = '2.1.0'
+        recipefile = os.path.join(temprecipe, 'python3-%s_%s.bb' % (pn, pv))
+        srcuri = 'https://files.pythonhosted.org/packages/b9/f3/ef59cee614d5e0accf6fd0cbba025b93b272e626ca89fb70a3e9187c5d15/%s-%s.tar.gz' % (pn, pv)
+        result = runCmd('recipetool create -o %s %s' % (temprecipe, srcuri))
+        self.assertTrue(os.path.isfile(recipefile))
+        checkvars = {}
+        checkvars['SUMMARY'] = 'Simple module to parse ISO 8601 dates'
+        checkvars['LICENSE'] = set(['MIT'])
+        checkvars['LIC_FILES_CHKSUM'] = 'file://LICENSE;md5=aab31f2ef7ba214a5a341eaa47a7f367'
+        checkvars['SRC_URI'] = 'https://files.pythonhosted.org/packages/b9/f3/ef59cee614d5e0accf6fd0cbba025b93b272e626ca89fb70a3e9187c5d15/iso8601-${PV}.tar.gz'
+        checkvars['SRC_URI[md5sum]'] = '6e33910eba87066b3be7fcf3d59d16b5'
+        checkvars['SRC_URI[sha1sum]'] = 'efd225b2c9fa7d9e4a1ec6ad94f3295cee982e61'
+        checkvars['SRC_URI[sha256sum]'] = '6b1d3829ee8921c4301998c909f7829fa9ed3cbdac0d3b16af2d743aed1ba8df'
+        checkvars['SRC_URI[sha384sum]'] = '255002433fe65c19adfd6b91494271b613cb25ef6a35ac77436de1e03d60cc07bf89fd716451b917f1435e4384860ef6'
+        checkvars['SRC_URI[sha512sum]'] = 'db57ab2a25ef91e3bc479c8539d27e853cf1fbf60986820b8999ae15d7e566425a1e0cfba47d0f3b23aa703db0576db368e6c110ba2a2f46c9a34e8ee3611fb7'
+        inherits = ['python_poetry_core']
+
+        self._test_recipe_contents(recipefile, checkvars, inherits)
+
+    def test_recipetool_create_python3_pep517_flit_core_buildapi(self):
+        # This test require python 3.11 or above for the tomllib module
+        # or tomli module to be installed
+        try:
+            import tomllib
+        except ImportError:
+            try:
+                import tomli
+            except ImportError:
+                self.skipTest('Test requires python 3.11 or above for tomllib module or tomli module')
+
+        # Test creating python3 package from tarball (using flit_core.buildapi class)
+        temprecipe = os.path.join(self.tempdir, 'recipe')
+        os.makedirs(temprecipe)
+        pn = 'typing-extensions'
+        pv = '4.8.0'
+        recipefile = os.path.join(temprecipe, 'python3-%s_%s.bb' % (pn, pv))
+        srcuri = 'https://files.pythonhosted.org/packages/1f/7a/8b94bb016069caa12fc9f587b28080ac33b4fbb8ca369b98bc0a4828543e/typing_extensions-%s.tar.gz' % pv
+        result = runCmd('recipetool create -o %s %s' % (temprecipe, srcuri))
+        self.assertTrue(os.path.isfile(recipefile))
+        checkvars = {}
+        checkvars['SUMMARY'] = 'Backported and Experimental Type Hints for Python 3.8+'
+        checkvars['LICENSE'] = set(['PSF-2.0'])
+        checkvars['LIC_FILES_CHKSUM'] = 'file://LICENSE;md5=fcf6b249c2641540219a727f35d8d2c2'
+        checkvars['SRC_URI'] = 'https://files.pythonhosted.org/packages/1f/7a/8b94bb016069caa12fc9f587b28080ac33b4fbb8ca369b98bc0a4828543e/typing_extensions-${PV}.tar.gz'
+        checkvars['SRC_URI[md5sum]'] = '74bafe841fbd1c27324afdeb099babdf'
+        checkvars['SRC_URI[sha1sum]'] = 'f8bed69cbad4a57a1a67bf8a31b62b657b47f7a3'
+        checkvars['SRC_URI[sha256sum]'] = 'df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef'
+        checkvars['SRC_URI[sha384sum]'] = '0bd0112234134d965c6884f3c1f95d27b6ae49cfb08108101158e31dff33c2dce729331628b69818850f1acb68f6c8d0'
+        checkvars['SRC_URI[sha512sum]'] = '5fbff10e085fbf3ac2e35d08d913608d8c8bca66903435ede91cdc7776d775689a53d64f5f0615fe687c6c228ac854c8651d99eb1cb96ec61c56b7ca01fdd440'
+        inherits = ['python_flit_core']
+
+        self._test_recipe_contents(recipefile, checkvars, inherits)
+
+    def test_recipetool_create_python3_pep517_hatchling(self):
+        # This test require python 3.11 or above for the tomllib module
+        # or tomli module to be installed
+        try:
+            import tomllib
+        except ImportError:
+            try:
+                import tomli
+            except ImportError:
+                self.skipTest('Test requires python 3.11 or above for tomllib module or tomli module')
+
+        # Test creating python3 package from tarball (using hatchling class)
+        temprecipe = os.path.join(self.tempdir, 'recipe')
+        os.makedirs(temprecipe)
+        pn = 'jsonschema'
+        pv = '4.19.1'
+        recipefile = os.path.join(temprecipe, 'python3-%s_%s.bb' % (pn, pv))
+        srcuri = 'https://files.pythonhosted.org/packages/e4/43/087b24516db11722c8687e0caf0f66c7785c0b1c51b0ab951dfde924e3f5/jsonschema-%s.tar.gz' % pv
+        result = runCmd('recipetool create -o %s %s' % (temprecipe, srcuri))
+        self.assertTrue(os.path.isfile(recipefile))
+        checkvars = {}
+        checkvars['SUMMARY'] = 'An implementation of JSON Schema validation for Python'
+        checkvars['HOMEPAGE'] = 'https://github.com/python-jsonschema/jsonschema'
+        checkvars['LICENSE'] = set(['MIT'])
+        checkvars['LIC_FILES_CHKSUM'] = 'file://COPYING;md5=7a60a81c146ec25599a3e1dabb8610a8 file://json/LICENSE;md5=9d4de43111d33570c8fe49b4cb0e01af'
+        checkvars['SRC_URI'] = 'https://files.pythonhosted.org/packages/e4/43/087b24516db11722c8687e0caf0f66c7785c0b1c51b0ab951dfde924e3f5/jsonschema-${PV}.tar.gz'
+        checkvars['SRC_URI[md5sum]'] = '4d6667ce76f820c35082c2d60a4896ab'
+        checkvars['SRC_URI[sha1sum]'] = '9173714cb88964d07f3a3f4fcaaef638b8ceac0c'
+        checkvars['SRC_URI[sha256sum]'] = 'ec84cc37cfa703ef7cd4928db24f9cb31428a5d0fa77747b8b51a847458e0bbf'
+        checkvars['SRC_URI[sha384sum]'] = '7a53181f0e679aa3dc3eb4d05a420877b7b9bff2d02e81f5c289a37ed1127d6c0cca1f5a5f9e4e166f089ab36bcc2be9'
+        checkvars['SRC_URI[sha512sum]'] = '60fa769faf6e3fc2c14eb9acd189c86e9d366b157230a5681d36552af0c159cb1ad33fd920668a36afdab98bc97253f91501704c5c07b5009fdaf9d29b52060d'
+        inherits = ['python_hatchling']
+
+        self._test_recipe_contents(recipefile, checkvars, inherits)
+
     def test_recipetool_create_github_tarball(self):
         # Basic test to ensure github URL mangling doesn't apply to release tarballs
         temprecipe = os.path.join(self.tempdir, 'recipe')
         os.makedirs(temprecipe)
         pv = '0.32.0'
-        recipefile = os.path.join(temprecipe, 'meson_%s.bb' % pv)
+        recipefile = os.path.join(temprecipe, 'python3-meson_%s.bb' % pv)
         srcuri = 'https://github.com/mesonbuild/meson/releases/download/%s/meson-%s.tar.gz' % (pv, pv)
         result = runCmd('recipetool create -o %s %s' % (temprecipe, srcuri))
         self.assertTrue(os.path.isfile(recipefile))
@@ -532,6 +669,191 @@
         libpath = os.path.join(get_bb_var('COREBASE'), 'scripts', 'lib', 'recipetool')
         sys.path.insert(0, libpath)
 
+    def test_recipetool_create_go(self):
+        # Basic test to check go recipe generation
+        def urifiy(url, version, modulepath = None, pathmajor = None, subdir = None):
+            modulepath = ",path='%s'" % modulepath if len(modulepath) else ''
+            pathmajor = ",pathmajor='%s'" % pathmajor if len(pathmajor) else ''
+            subdir = ",subdir='%s'" % subdir if len(subdir) else ''
+            return "${@go_src_uri('%s','%s'%s%s%s)}" % (url, version, modulepath, pathmajor, subdir)
+
+        temprecipe = os.path.join(self.tempdir, 'recipe')
+        os.makedirs(temprecipe)
+
+        recipefile = os.path.join(temprecipe, 'edgex-go_git.bb')
+        deps_require_file = os.path.join(temprecipe, 'edgex-go', 'edgex-go-modules.inc')
+        lics_require_file = os.path.join(temprecipe, 'edgex-go', 'edgex-go-licenses.inc')
+        modules_txt_file = os.path.join(temprecipe, 'edgex-go', 'modules.txt')
+
+        srcuri = 'https://github.com/edgexfoundry/edgex-go.git'
+        srcrev = "v3.0.0"
+        srcbranch = "main"
+
+        result = runCmd('recipetool create -o %s %s -S %s -B %s' % (temprecipe, srcuri, srcrev, srcbranch))
+
+        self.maxDiff = None
+        inherits = ['go-vendor']
+
+        checkvars = {}
+        checkvars['GO_IMPORT'] = "github.com/edgexfoundry/edgex-go"
+        checkvars['SRC_URI'] = {'git://${GO_IMPORT};destsuffix=git/src/${GO_IMPORT};nobranch=1;name=${BPN};protocol=https',
+                                'file://modules.txt'}
+        checkvars['LIC_FILES_CHKSUM'] = {'file://src/${GO_IMPORT}/LICENSE;md5=8f8bc924cf73f6a32381e5fd4c58d603'}
+
+        self.assertTrue(os.path.isfile(recipefile))
+        self._test_recipe_contents(recipefile, checkvars, inherits)
+
+        checkvars = {}
+        checkvars['VENDORED_LIC_FILES_CHKSUM'] = set(
+                 ['file://src/${GO_IMPORT}/vendor/github.com/Microsoft/go-winio/LICENSE;md5=69205ff73858f2c22b2ca135b557e8ef',
+                 'file://src/${GO_IMPORT}/vendor/github.com/armon/go-metrics/LICENSE;md5=d2d77030c0183e3d1e66d26dc1f243be',
+                 'file://src/${GO_IMPORT}/vendor/github.com/cenkalti/backoff/LICENSE;md5=1571d94433e3f3aa05267efd4dbea68b',
+                 'file://src/${GO_IMPORT}/vendor/github.com/davecgh/go-spew/LICENSE;md5=c06795ed54b2a35ebeeb543cd3a73e56',
+                 'file://src/${GO_IMPORT}/vendor/github.com/eclipse/paho.mqtt.golang/LICENSE;md5=dcdb33474b60c38efd27356d8f2edec7',
+                 'file://src/${GO_IMPORT}/vendor/github.com/eclipse/paho.mqtt.golang/edl-v10;md5=3adfcc70f5aeb7a44f3f9b495aa1fbf3',
+                 'file://src/${GO_IMPORT}/vendor/github.com/edgexfoundry/go-mod-bootstrap/v3/LICENSE;md5=0d6dae39976133b2851fba4c1e1275ff',
+                 'file://src/${GO_IMPORT}/vendor/github.com/edgexfoundry/go-mod-configuration/v3/LICENSE;md5=0d6dae39976133b2851fba4c1e1275ff',
+                 'file://src/${GO_IMPORT}/vendor/github.com/edgexfoundry/go-mod-core-contracts/v3/LICENSE;md5=0d6dae39976133b2851fba4c1e1275ff',
+                 'file://src/${GO_IMPORT}/vendor/github.com/edgexfoundry/go-mod-messaging/v3/LICENSE;md5=0d6dae39976133b2851fba4c1e1275ff',
+                 'file://src/${GO_IMPORT}/vendor/github.com/edgexfoundry/go-mod-registry/v3/LICENSE;md5=0d6dae39976133b2851fba4c1e1275ff',
+                 'file://src/${GO_IMPORT}/vendor/github.com/edgexfoundry/go-mod-secrets/v3/LICENSE;md5=f9fa2f4f8e0ef8cc7b5dd150963eb457',
+                 'file://src/${GO_IMPORT}/vendor/github.com/fatih/color/LICENSE.md;md5=316e6d590bdcde7993fb175662c0dd5a',
+                 'file://src/${GO_IMPORT}/vendor/github.com/fxamacker/cbor/v2/LICENSE;md5=827f5a2fa861382d35a3943adf9ebb86',
+                 'file://src/${GO_IMPORT}/vendor/github.com/go-jose/go-jose/v3/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57',
+                 'file://src/${GO_IMPORT}/vendor/github.com/go-jose/go-jose/v3/json/LICENSE;md5=591778525c869cdde0ab5a1bf283cd81',
+                 'file://src/${GO_IMPORT}/vendor/github.com/go-kit/log/LICENSE;md5=5b7c15ad5fffe2ff6e9d58a6c161f082',
+                 'file://src/${GO_IMPORT}/vendor/github.com/go-logfmt/logfmt/LICENSE;md5=98e39517c38127f969de33057067091e',
+                 'file://src/${GO_IMPORT}/vendor/github.com/go-playground/locales/LICENSE;md5=3ccbda375ee345400ad1da85ba522301',
+                 'file://src/${GO_IMPORT}/vendor/github.com/go-playground/universal-translator/LICENSE;md5=2e2b21ef8f61057977d27c727c84bef1',
+                 'file://src/${GO_IMPORT}/vendor/github.com/go-playground/validator/v10/LICENSE;md5=a718a0f318d76f7c5d510cbae84f0b60',
+                 'file://src/${GO_IMPORT}/vendor/github.com/go-redis/redis/v7/LICENSE;md5=58103aa5ea1ee9b7a369c9c4a95ef9b5',
+                 'file://src/${GO_IMPORT}/vendor/github.com/golang/protobuf/LICENSE;md5=939cce1ec101726fa754e698ac871622',
+                 'file://src/${GO_IMPORT}/vendor/github.com/gomodule/redigo/LICENSE;md5=2ee41112a44fe7014dce33e26468ba93',
+                 'file://src/${GO_IMPORT}/vendor/github.com/google/uuid/LICENSE;md5=88073b6dd8ec00fe09da59e0b6dfded1',
+                 'file://src/${GO_IMPORT}/vendor/github.com/gorilla/mux/LICENSE;md5=33fa1116c45f9e8de714033f99edde13',
+                 'file://src/${GO_IMPORT}/vendor/github.com/gorilla/websocket/LICENSE;md5=c007b54a1743d596f46b2748d9f8c044',
+                 'file://src/${GO_IMPORT}/vendor/github.com/hashicorp/consul/api/LICENSE;md5=b8a277a612171b7526e9be072f405ef4',
+                 'file://src/${GO_IMPORT}/vendor/github.com/hashicorp/errwrap/LICENSE;md5=b278a92d2c1509760384428817710378',
+                 'file://src/${GO_IMPORT}/vendor/github.com/hashicorp/go-cleanhttp/LICENSE;md5=65d26fcc2f35ea6a181ac777e42db1ea',
+                 'file://src/${GO_IMPORT}/vendor/github.com/hashicorp/go-hclog/LICENSE;md5=ec7f605b74b9ad03347d0a93a5cc7eb8',
+                 'file://src/${GO_IMPORT}/vendor/github.com/hashicorp/go-immutable-radix/LICENSE;md5=65d26fcc2f35ea6a181ac777e42db1ea',
+                 'file://src/${GO_IMPORT}/vendor/github.com/hashicorp/go-multierror/LICENSE;md5=d44fdeb607e2d2614db9464dbedd4094',
+                 'file://src/${GO_IMPORT}/vendor/github.com/hashicorp/go-rootcerts/LICENSE;md5=65d26fcc2f35ea6a181ac777e42db1ea',
+                 'file://src/${GO_IMPORT}/vendor/github.com/hashicorp/golang-lru/LICENSE;md5=f27a50d2e878867827842f2c60e30bfc',
+                 'file://src/${GO_IMPORT}/vendor/github.com/hashicorp/serf/LICENSE;md5=b278a92d2c1509760384428817710378',
+                 'file://src/${GO_IMPORT}/vendor/github.com/leodido/go-urn/LICENSE;md5=8f50db5538ec1148a9b3d14ed96c3418',
+                 'file://src/${GO_IMPORT}/vendor/github.com/mattn/go-colorable/LICENSE;md5=24ce168f90aec2456a73de1839037245',
+                 'file://src/${GO_IMPORT}/vendor/github.com/mattn/go-isatty/LICENSE;md5=f509beadd5a11227c27b5d2ad6c9f2c6',
+                 'file://src/${GO_IMPORT}/vendor/github.com/mitchellh/consulstructure/LICENSE;md5=96ada10a9e51c98c4656f2cede08c673',
+                 'file://src/${GO_IMPORT}/vendor/github.com/mitchellh/copystructure/LICENSE;md5=56da355a12d4821cda57b8f23ec34bc4',
+                 'file://src/${GO_IMPORT}/vendor/github.com/mitchellh/go-homedir/LICENSE;md5=3f7765c3d4f58e1f84c4313cecf0f5bd',
+                 'file://src/${GO_IMPORT}/vendor/github.com/mitchellh/mapstructure/LICENSE;md5=3f7765c3d4f58e1f84c4313cecf0f5bd',
+                 'file://src/${GO_IMPORT}/vendor/github.com/mitchellh/reflectwalk/LICENSE;md5=3f7765c3d4f58e1f84c4313cecf0f5bd',
+                 'file://src/${GO_IMPORT}/vendor/github.com/nats-io/nats.go/LICENSE;md5=86d3f3a95c324c9479bd8986968f4327',
+                 'file://src/${GO_IMPORT}/vendor/github.com/nats-io/nkeys/LICENSE;md5=86d3f3a95c324c9479bd8986968f4327',
+                 'file://src/${GO_IMPORT}/vendor/github.com/nats-io/nuid/LICENSE;md5=86d3f3a95c324c9479bd8986968f4327',
+                 'file://src/${GO_IMPORT}/vendor/github.com/pmezard/go-difflib/LICENSE;md5=e9a2ebb8de779a07500ddecca806145e',
+                 'file://src/${GO_IMPORT}/vendor/github.com/rcrowley/go-metrics/LICENSE;md5=1bdf5d819f50f141366dabce3be1460f',
+                 'file://src/${GO_IMPORT}/vendor/github.com/spiffe/go-spiffe/v2/LICENSE;md5=86d3f3a95c324c9479bd8986968f4327',
+                 'file://src/${GO_IMPORT}/vendor/github.com/stretchr/objx/LICENSE;md5=d023fd31d3ca39ec61eec65a91732735',
+                 'file://src/${GO_IMPORT}/vendor/github.com/stretchr/testify/LICENSE;md5=188f01994659f3c0d310612333d2a26f',
+                 'file://src/${GO_IMPORT}/vendor/github.com/x448/float16/LICENSE;md5=de8f8e025d57fe7ee0b67f30d571323b',
+                 'file://src/${GO_IMPORT}/vendor/github.com/zeebo/errs/LICENSE;md5=84914ab36fc0eb48edbaa53e66e8d326',
+                 'file://src/${GO_IMPORT}/vendor/golang.org/x/crypto/LICENSE;md5=5d4950ecb7b26d2c5e4e7b4e0dd74707',
+                 'file://src/${GO_IMPORT}/vendor/golang.org/x/mod/LICENSE;md5=5d4950ecb7b26d2c5e4e7b4e0dd74707',
+                 'file://src/${GO_IMPORT}/vendor/golang.org/x/net/LICENSE;md5=5d4950ecb7b26d2c5e4e7b4e0dd74707',
+                 'file://src/${GO_IMPORT}/vendor/golang.org/x/sync/LICENSE;md5=5d4950ecb7b26d2c5e4e7b4e0dd74707',
+                 'file://src/${GO_IMPORT}/vendor/golang.org/x/sys/LICENSE;md5=5d4950ecb7b26d2c5e4e7b4e0dd74707',
+                 'file://src/${GO_IMPORT}/vendor/golang.org/x/text/LICENSE;md5=5d4950ecb7b26d2c5e4e7b4e0dd74707',
+                 'file://src/${GO_IMPORT}/vendor/golang.org/x/tools/LICENSE;md5=5d4950ecb7b26d2c5e4e7b4e0dd74707',
+                 'file://src/${GO_IMPORT}/vendor/google.golang.org/genproto/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57',
+                 'file://src/${GO_IMPORT}/vendor/google.golang.org/grpc/LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57',
+                 'file://src/${GO_IMPORT}/vendor/google.golang.org/protobuf/LICENSE;md5=02d4002e9171d41a8fad93aa7faf3956',
+                 'file://src/${GO_IMPORT}/vendor/gopkg.in/eapache/queue.v1/LICENSE;md5=1bfd4408d3de090ef6b908b0cc45a316',
+                 'file://src/${GO_IMPORT}/vendor/gopkg.in/yaml.v3/LICENSE;md5=3c91c17266710e16afdbb2b6d15c761c'])
+
+        self.assertTrue(os.path.isfile(lics_require_file))
+        self._test_recipe_contents(lics_require_file, checkvars, [])
+
+        dependencies = \
+            [   ('github.com/eclipse/paho.mqtt.golang','v1.4.2', '', '', ''),
+                ('github.com/edgexfoundry/go-mod-bootstrap','v3.0.1','github.com/edgexfoundry/go-mod-bootstrap/v3','/v3', ''),
+                ('github.com/edgexfoundry/go-mod-configuration','v3.0.0','github.com/edgexfoundry/go-mod-configuration/v3','/v3', ''),
+                ('github.com/edgexfoundry/go-mod-core-contracts','v3.0.0','github.com/edgexfoundry/go-mod-core-contracts/v3','/v3', ''),
+                ('github.com/edgexfoundry/go-mod-messaging','v3.0.0','github.com/edgexfoundry/go-mod-messaging/v3','/v3', ''),
+                ('github.com/edgexfoundry/go-mod-secrets','v3.0.1','github.com/edgexfoundry/go-mod-secrets/v3','/v3', ''),
+                ('github.com/fxamacker/cbor','v2.4.0','github.com/fxamacker/cbor/v2','/v2', ''),
+                ('github.com/gomodule/redigo','v1.8.9', '', '', ''),
+                ('github.com/google/uuid','v1.3.0', '', '', ''),
+                ('github.com/gorilla/mux','v1.8.0', '', '', ''),
+                ('github.com/rcrowley/go-metrics','v0.0.0-20201227073835-cf1acfcdf475', '', '', ''),
+                ('github.com/spiffe/go-spiffe','v2.1.4','github.com/spiffe/go-spiffe/v2','/v2', ''),
+                ('github.com/stretchr/testify','v1.8.2', '', '', ''),
+                ('go.googlesource.com/crypto','v0.8.0','golang.org/x/crypto', '', ''),
+                ('gopkg.in/eapache/queue.v1','v1.1.0', '', '', ''),
+                ('gopkg.in/yaml.v3','v3.0.1', '', '', ''),
+                ('github.com/microsoft/go-winio','v0.6.0','github.com/Microsoft/go-winio', '', ''),
+                ('github.com/hashicorp/go-metrics','v0.3.10','github.com/armon/go-metrics', '', ''),
+                ('github.com/cenkalti/backoff','v2.2.1+incompatible', '', '', ''),
+                ('github.com/davecgh/go-spew','v1.1.1', '', '', ''),
+                ('github.com/edgexfoundry/go-mod-registry','v3.0.0','github.com/edgexfoundry/go-mod-registry/v3','/v3', ''),
+                ('github.com/fatih/color','v1.9.0', '', '', ''),
+                ('github.com/go-jose/go-jose','v3.0.0','github.com/go-jose/go-jose/v3','/v3', ''),
+                ('github.com/go-kit/log','v0.2.1', '', '', ''),
+                ('github.com/go-logfmt/logfmt','v0.5.1', '', '', ''),
+                ('github.com/go-playground/locales','v0.14.1', '', '', ''),
+                ('github.com/go-playground/universal-translator','v0.18.1', '', '', ''),
+                ('github.com/go-playground/validator','v10.13.0','github.com/go-playground/validator/v10','/v10', ''),
+                ('github.com/go-redis/redis','v7.3.0','github.com/go-redis/redis/v7','/v7', ''),
+                ('github.com/golang/protobuf','v1.5.2', '', '', ''),
+                ('github.com/gorilla/websocket','v1.4.2', '', '', ''),
+                ('github.com/hashicorp/consul','v1.20.0','github.com/hashicorp/consul/api', '', 'api'),
+                ('github.com/hashicorp/errwrap','v1.0.0', '', '', ''),
+                ('github.com/hashicorp/go-cleanhttp','v0.5.1', '', '', ''),
+                ('github.com/hashicorp/go-hclog','v0.14.1', '', '', ''),
+                ('github.com/hashicorp/go-immutable-radix','v1.3.0', '', '', ''),
+                ('github.com/hashicorp/go-multierror','v1.1.1', '', '', ''),
+                ('github.com/hashicorp/go-rootcerts','v1.0.2', '', '', ''),
+                ('github.com/hashicorp/golang-lru','v0.5.4', '', '', ''),
+                ('github.com/hashicorp/serf','v0.10.1', '', '', ''),
+                ('github.com/leodido/go-urn','v1.2.3', '', '', ''),
+                ('github.com/mattn/go-colorable','v0.1.12', '', '', ''),
+                ('github.com/mattn/go-isatty','v0.0.14', '', '', ''),
+                ('github.com/mitchellh/consulstructure','v0.0.0-20190329231841-56fdc4d2da54', '', '', ''),
+                ('github.com/mitchellh/copystructure','v1.2.0', '', '', ''),
+                ('github.com/mitchellh/go-homedir','v1.1.0', '', '', ''),
+                ('github.com/mitchellh/mapstructure','v1.5.0', '', '', ''),
+                ('github.com/mitchellh/reflectwalk','v1.0.2', '', '', ''),
+                ('github.com/nats-io/nats.go','v1.25.0', '', '', ''),
+                ('github.com/nats-io/nkeys','v0.4.4', '', '', ''),
+                ('github.com/nats-io/nuid','v1.0.1', '', '', ''),
+                ('github.com/pmezard/go-difflib','v1.0.0', '', '', ''),
+                ('github.com/stretchr/objx','v0.5.0', '', '', ''),
+                ('github.com/x448/float16','v0.8.4', '', '', ''),
+                ('github.com/zeebo/errs','v1.3.0', '', '', ''),
+                ('go.googlesource.com/mod','v0.8.0','golang.org/x/mod', '', ''),
+                ('go.googlesource.com/net','v0.9.0','golang.org/x/net', '', ''),
+                ('go.googlesource.com/sync','v0.1.0','golang.org/x/sync', '', ''),
+                ('go.googlesource.com/sys','v0.7.0','golang.org/x/sys', '', ''),
+                ('go.googlesource.com/text','v0.9.0','golang.org/x/text', '', ''),
+                ('go.googlesource.com/tools','v0.6.0','golang.org/x/tools', '', ''),
+                ('github.com/googleapis/go-genproto','v0.0.0-20230223222841-637eb2293923','google.golang.org/genproto', '', ''),
+                ('github.com/grpc/grpc-go','v1.53.0','google.golang.org/grpc', '', ''),
+                ('go.googlesource.com/protobuf','v1.28.1','google.golang.org/protobuf', '', ''),
+            ]
+
+        src_uri = set()
+        for d in dependencies:
+            src_uri.add(urifiy(*d))
+
+        checkvars = {}
+        checkvars['GO_DEPENDENCIES_SRC_URI'] = src_uri
+
+        self.assertTrue(os.path.isfile(deps_require_file))
+        self._test_recipe_contents(deps_require_file, checkvars, [])
+
+
+        
     def _copy_file_with_cleanup(self, srcfile, basedstdir, *paths):
         dstdir = basedstdir
         self.assertTrue(os.path.exists(dstdir))
diff --git a/poky/meta/lib/oeqa/selftest/cases/reproducible.py b/poky/meta/lib/oeqa/selftest/cases/reproducible.py
index 9b4a088..029b6af 100644
--- a/poky/meta/lib/oeqa/selftest/cases/reproducible.py
+++ b/poky/meta/lib/oeqa/selftest/cases/reproducible.py
@@ -16,6 +16,8 @@
 import datetime
 
 exclude_packages = [
+	'rust',
+	'rust-dbg'
 	]
 
 def is_excluded(package):
@@ -43,13 +45,14 @@
         return (self.status, self.test) < (other.status, other.test)
 
 class PackageCompareResults(object):
-    def __init__(self):
+    def __init__(self, exclusions):
         self.total = []
         self.missing = []
         self.different = []
         self.different_excluded = []
         self.same = []
         self.active_exclusions = set()
+        exclude_packages.extend((exclusions or "").split())
 
     def add_result(self, r):
         self.total.append(r)
@@ -151,7 +154,16 @@
 
     def setUpLocal(self):
         super().setUpLocal()
-        needed_vars = ['TOPDIR', 'TARGET_PREFIX', 'BB_NUMBER_THREADS', 'BB_HASHSERVE', 'OEQA_REPRODUCIBLE_TEST_PACKAGE', 'OEQA_REPRODUCIBLE_TEST_TARGET', 'OEQA_REPRODUCIBLE_TEST_SSTATE_TARGETS']
+        needed_vars = [
+            'TOPDIR',
+            'TARGET_PREFIX',
+            'BB_NUMBER_THREADS',
+            'BB_HASHSERVE',
+            'OEQA_REPRODUCIBLE_TEST_PACKAGE',
+            'OEQA_REPRODUCIBLE_TEST_TARGET',
+            'OEQA_REPRODUCIBLE_TEST_SSTATE_TARGETS',
+            'OEQA_REPRODUCIBLE_EXCLUDED_PACKAGES',
+        ]
         bb_vars = get_bb_vars(needed_vars)
         for v in needed_vars:
             setattr(self, v.lower(), bb_vars[v])
@@ -173,7 +185,7 @@
         self.extraresults['reproducible.rawlogs']['log'] += msg
 
     def compare_packages(self, reference_dir, test_dir, diffutils_sysroot):
-        result = PackageCompareResults()
+        result = PackageCompareResults(self.oeqa_reproducible_excluded_packages)
 
         old_cwd = os.getcwd()
         try:
diff --git a/poky/meta/lib/oeqa/selftest/cases/signing.py b/poky/meta/lib/oeqa/selftest/cases/signing.py
index 322e753..18cce0b 100644
--- a/poky/meta/lib/oeqa/selftest/cases/signing.py
+++ b/poky/meta/lib/oeqa/selftest/cases/signing.py
@@ -191,7 +191,7 @@
 
         bitbake(test_recipe)
         # Generate locked sigs include file
-        bitbake('-S none %s' % test_recipe)
+        bitbake('-S lockedsigs %s' % test_recipe)
 
         feature = 'require %s\n' % locked_sigs_file
         feature += 'SIGGEN_LOCKEDSIGS_TASKSIG_CHECK = "warn"\n'
diff --git a/poky/meta/lib/oeqa/selftest/cases/sstatetests.py b/poky/meta/lib/oeqa/selftest/cases/sstatetests.py
index bdad908..7c2b14e 100644
--- a/poky/meta/lib/oeqa/selftest/cases/sstatetests.py
+++ b/poky/meta/lib/oeqa/selftest/cases/sstatetests.py
@@ -14,6 +14,7 @@
 
 from oeqa.utils.commands import runCmd, bitbake, get_bb_var, create_temp_layer, get_bb_vars
 from oeqa.selftest.case import OESelftestTestCase
+from oeqa.core.decorator import OETestTag
 
 import oe
 import bb.siggen
@@ -773,3 +774,159 @@
                 latestfiles = sorted(filedates.keys(), key=lambda f: filedates[f])[-2:]
                 bb.siggen.compare_sigfiles(latestfiles[-2], latestfiles[-1], recursecb)
                 self.assertEqual(recursecb_count,1)
+
+class SStatePrintdiff(SStateBase):
+    def run_test_printdiff_changerecipe(self, target, change_recipe, change_bbtask, change_content, expected_sametmp_output, expected_difftmp_output):
+        self.write_config("""
+TMPDIR = "${TOPDIR}/tmp-sstateprintdiff"
+""")
+        self.track_for_cleanup(self.topdir + "/tmp-sstateprintdiff")
+        # Use runall do_build to ensure any indirect sstate is created, e.g. tzcode-native on both x86 and
+        # aarch64 hosts since only allarch target recipes depend upon it and it may not be built otherwise.
+        # A bitbake -c cleansstate tzcode-native would cause some of these tests to error for example.
+        bitbake("--runall build --runall deploy_source_date_epoch {}".format(target))
+        bitbake("-S none {}".format(target))
+        bitbake(change_bbtask)
+        self.write_recipeinc(change_recipe, change_content)
+        result_sametmp = bitbake("-S printdiff {}".format(target))
+
+        self.write_config("""
+TMPDIR = "${TOPDIR}/tmp-sstateprintdiff-2"
+""")
+        self.track_for_cleanup(self.topdir + "/tmp-sstateprintdiff-2")
+        result_difftmp = bitbake("-S printdiff {}".format(target))
+
+        self.delete_recipeinc(change_recipe)
+        for item in expected_sametmp_output:
+            self.assertIn(item, result_sametmp.output)
+        for item in expected_difftmp_output:
+            self.assertIn(item, result_difftmp.output)
+
+    def run_test_printdiff_changeconfig(self, target, change_content, expected_sametmp_output, expected_difftmp_output):
+        self.write_config("""
+TMPDIR = "${TOPDIR}/tmp-sstateprintdiff"
+""")
+        self.track_for_cleanup(self.topdir + "/tmp-sstateprintdiff")
+        bitbake("--runall build --runall deploy_source_date_epoch {}".format(target))
+        bitbake("-S none {}".format(target))
+        self.append_config(change_content)
+        result_sametmp = bitbake("-S printdiff {}".format(target))
+
+        self.write_config("""
+TMPDIR = "${TOPDIR}/tmp-sstateprintdiff-2"
+""")
+        self.append_config(change_content)
+        self.track_for_cleanup(self.topdir + "/tmp-sstateprintdiff-2")
+        result_difftmp = bitbake("-S printdiff {}".format(target))
+
+        for item in expected_sametmp_output:
+            self.assertIn(item, result_sametmp.output)
+        for item in expected_difftmp_output:
+            self.assertIn(item, result_difftmp.output)
+
+
+    # Check if printdiff walks the full dependency chain from the image target to where the change is in a specific recipe
+    def test_image_minimal_vs_quilt(self):
+        expected_output = ("Task quilt-native:do_install couldn't be used from the cache because:",
+"We need hash",
+"most recent matching task was")
+        expected_sametmp_output = expected_output + ("Variable do_install value changed",'+    echo "this changes the task signature"')
+        expected_difftmp_output = expected_output
+
+        self.run_test_printdiff_changerecipe("core-image-minimal", "quilt-native", "-c do_install quilt-native",
+"""
+do_install:append() {
+    echo "this changes the task signature"
+}
+""",
+expected_sametmp_output, expected_difftmp_output)
+
+    # Check if changes to gcc-source (which uses tmp/work-shared) are correctly discovered
+    def test_gcc_runtime_vs_gcc_source(self):
+        gcc_source_pn = 'gcc-source-%s' % get_bb_vars(['PV'], 'gcc')['PV']
+
+        expected_output = ("Task {}:do_preconfigure couldn't be used from the cache because:".format(gcc_source_pn),
+"We need hash",
+"most recent matching task was")
+        expected_sametmp_output = expected_output + ("Variable do_preconfigure value changed",'+    print("this changes the task signature")')
+        #FIXME: printdiff is supposed to find at least one preconfigure task signature in the sstate cache, but isn't able to
+        #expected_difftmp_output = expected_output
+        expected_difftmp_output = ()
+
+        self.run_test_printdiff_changerecipe("gcc-runtime", "gcc-source", "-c do_preconfigure {}".format(gcc_source_pn),
+"""
+python do_preconfigure:append() {
+    print("this changes the task signature")
+}
+""",
+expected_sametmp_output, expected_difftmp_output)
+
+    # Check if changing a really base task definiton is reported against multiple core recipes using it
+    def test_image_minimal_vs_base_do_configure(self):
+        expected_output = ("Task zstd-native:do_configure couldn't be used from the cache because:",
+"Task texinfo-dummy-native:do_configure couldn't be used from the cache because:",
+"Task ldconfig-native:do_configure couldn't be used from the cache because:",
+"Task gettext-minimal-native:do_configure couldn't be used from the cache because:",
+"Task tzcode-native:do_configure couldn't be used from the cache because:",
+"Task makedevs-native:do_configure couldn't be used from the cache because:",
+"Task pigz-native:do_configure couldn't be used from the cache because:",
+"Task update-rc.d-native:do_configure couldn't be used from the cache because:",
+"Task unzip-native:do_configure couldn't be used from the cache because:",
+"Task gnu-config-native:do_configure couldn't be used from the cache because:",
+"We need hash",
+"most recent matching task was")
+        expected_sametmp_output = expected_output + ("Variable base_do_configure value changed",'+	echo "this changes base_do_configure() definiton "')
+        expected_difftmp_output = expected_output
+
+        self.run_test_printdiff_changeconfig("core-image-minimal",
+"""
+INHERIT += "base-do-configure-modified"
+""",
+expected_sametmp_output, expected_difftmp_output)
+
+@OETestTag("yocto-mirrors")
+class SStateMirrors(SStateBase):
+    def check_bb_output(self, output, exceptions):
+        in_tasks = False
+        missing_objects = []
+        checked_urls = []
+        for l in output.splitlines():
+            if "Testing URL" in l:
+                checked_urls.append(l.split()[3])
+            if "The differences between the current build and any cached tasks start at the following tasks" in l:
+                in_tasks = True
+                continue
+            if "Writing task signature files" in l:
+                in_tasks = False
+                continue
+            if in_tasks:
+                recipe_task = l.split("/")[-1]
+                recipe, task = recipe_task.split(":")
+                for e in exceptions:
+                    if e[0] in recipe and task == e[1]:
+                        break
+                else:
+                    missing_objects.append(recipe_task)
+        self.assertTrue(len(missing_objects) == 0, "URLs checked:\n{}\nMissing objects in the cache:\n{}".format("\n".join(checked_urls), "\n".join(missing_objects)))
+
+    def run_test_cdn_mirror(self, machine, targets, exceptions):
+        exceptions = exceptions + [[t, "do_deploy_source_date_epoch"] for t in targets.split()]
+        exceptions = exceptions + [[t, "do_image_qa"] for t in targets.split()]
+        self.config_sstate(True)
+        self.append_config("""
+MACHINE = "{}"
+BB_HASHSERVE_UPSTREAM = "hashserv.yocto.io:8687"
+SSTATE_MIRRORS ?= "file://.* http://cdn.jsdelivr.net/yocto/sstate/all/PATH;downloadfilename=PATH"
+""".format(machine))
+        result = bitbake("-D -S printdiff {}".format(targets))
+        self.check_bb_output(result.output, exceptions)
+
+    def test_cdn_mirror_qemux86_64(self):
+        # Example:
+        # exceptions = [ ["packagegroup-core-sdk","do_package"] ]
+        exceptions = []
+        self.run_test_cdn_mirror("qemux86-64", "core-image-minimal core-image-full-cmdline core-image-sato-sdk", exceptions)
+
+    def test_cdn_mirror_qemuarm64(self):
+        exceptions = []
+        self.run_test_cdn_mirror("qemuarm64", "core-image-minimal core-image-full-cmdline core-image-sato-sdk", exceptions)
diff --git a/poky/meta/lib/oeqa/selftest/cases/tinfoil.py b/poky/meta/lib/oeqa/selftest/cases/tinfoil.py
index dd13c20..21c8686 100644
--- a/poky/meta/lib/oeqa/selftest/cases/tinfoil.py
+++ b/poky/meta/lib/oeqa/selftest/cases/tinfoil.py
@@ -48,6 +48,17 @@
             rd = tinfoil.parse_recipe_file(best[3])
             self.assertEqual(testrecipe, rd.getVar('PN'))
 
+    def test_parse_virtual_recipe(self):
+        with bb.tinfoil.Tinfoil() as tinfoil:
+            tinfoil.prepare(config_only=False, quiet=2)
+            testrecipe = 'nativesdk-gcc'
+            best = tinfoil.find_best_provider(testrecipe)
+            if not best:
+                self.fail('Unable to find recipe providing %s' % testrecipe)
+            rd = tinfoil.parse_recipe_file(best[3])
+            self.assertEqual(testrecipe, rd.getVar('PN'))
+            self.assertIsNotNone(rd.getVar('FILE_LAYERNAME'))
+
     def test_parse_recipe_copy_expand(self):
         with bb.tinfoil.Tinfoil() as tinfoil:
             tinfoil.prepare(config_only=False, quiet=2)
@@ -66,7 +77,7 @@
             localdata.setVar('PN', 'hello')
             self.assertEqual('hello', localdata.getVar('BPN'))
 
-    # The config_data API tp parse_recipe_file is used by:
+    # The config_data API to parse_recipe_file is used by:
     # layerindex-web layerindex/update_layer.py
     def test_parse_recipe_custom_data(self):
         with bb.tinfoil.Tinfoil() as tinfoil:
@@ -80,6 +91,18 @@
             rd = tinfoil.parse_recipe_file(best[3], config_data=localdata)
             self.assertEqual("testval", rd.getVar('TESTVAR'))
 
+    def test_parse_virtual_recipe_custom_data(self):
+        with bb.tinfoil.Tinfoil() as tinfoil:
+            tinfoil.prepare(config_only=False, quiet=2)
+            localdata = bb.data.createCopy(tinfoil.config_data)
+            localdata.setVar("TESTVAR", "testval")
+            testrecipe = 'nativesdk-gcc'
+            best = tinfoil.find_best_provider(testrecipe)
+            if not best:
+                self.fail('Unable to find recipe providing %s' % testrecipe)
+            rd = tinfoil.parse_recipe_file(best[3], config_data=localdata)
+            self.assertEqual("testval", rd.getVar('TESTVAR'))
+
     def test_list_recipes(self):
         with bb.tinfoil.Tinfoil() as tinfoil:
             tinfoil.prepare(config_only=False, quiet=2)
diff --git a/poky/meta/lib/oeqa/selftest/context.py b/poky/meta/lib/oeqa/selftest/context.py
index 5a09aee..57844b2 100644
--- a/poky/meta/lib/oeqa/selftest/context.py
+++ b/poky/meta/lib/oeqa/selftest/context.py
@@ -70,8 +70,6 @@
     def __init__(self, td=None, logger=None, machines=None, config_paths=None, newbuilddir=None, keep_builddir=None):
         super(OESelftestTestContext, self).__init__(td, logger)
 
-        self.machines = machines
-        self.custommachine = None
         self.config_paths = config_paths
         self.newbuilddir = newbuilddir
 
@@ -160,12 +158,6 @@
             return NonConcurrentTestSuite(suites, processes, self.setup_builddir, self.removebuilddir, self.bb_vars)
 
     def runTests(self, processes=None, machine=None, skips=[]):
-        if machine:
-            self.custommachine = machine
-            if machine == 'random':
-                self.custommachine = choice(self.machines)
-            self.logger.info('Run tests with custom MACHINE set to: %s' % \
-                    self.custommachine)
         return super(OESelftestTestContext, self).runTests(processes, skips)
 
     def listTests(self, display_type, machine=None):
@@ -205,9 +197,6 @@
         parser.add_argument('-j', '--num-processes', dest='processes', action='store',
                 type=int, help="number of processes to execute in parallel with")
 
-        parser.add_argument('--machine', required=False, choices=['random', 'all'],
-                            help='Run tests on different machines (random/all).')
-
         parser.add_argument('-t', '--select-tag', dest="select_tags",
                 action='append', default=None,
                 help='Filter all (unhidden) tests to any that match any of the specified tag(s).')
@@ -222,20 +211,6 @@
         parser.add_argument('-v', '--verbose', action='store_true')
         parser.set_defaults(func=self.run)
 
-    def _get_available_machines(self):
-        machines = []
-
-        bbpath = self.tc_kwargs['init']['td']['BBPATH'].split(':')
-
-        for path in bbpath:
-            found_machines = glob.glob(os.path.join(path, 'conf', 'machine', '*.conf'))
-            if found_machines:
-                for i in found_machines:
-                    # eg: '/home/<user>/poky/meta-intel/conf/machine/intel-core2-32.conf'
-                    machines.append(os.path.splitext(os.path.basename(i))[0])
-
-        return machines
-
     def _get_cases_paths(self, bbpath):
         cases_paths = []
         for layer in bbpath:
@@ -266,7 +241,6 @@
             args.list_tests = 'name'
 
         self.tc_kwargs['init']['td'] = bbvars
-        self.tc_kwargs['init']['machines'] = self._get_available_machines()
 
         builddir = os.environ.get("BUILDDIR")
         self.tc_kwargs['init']['config_paths'] = {}
@@ -414,30 +388,7 @@
 
         rc = None
         try:
-            if args.machine:
-                logger.info('Custom machine mode enabled. MACHINE set to %s' %
-                        args.machine)
-
-                if args.machine == 'all':
-                    results = []
-                    for m in self.tc_kwargs['init']['machines']:
-                        self.tc_kwargs['run']['machine'] = m
-                        results.append(self._internal_run(logger, args))
-
-                        # XXX: the oe-selftest script only needs to know if one
-                        # machine run fails
-                        for r in results:
-                            rc = r
-                            if not r.wasSuccessful():
-                                break
-
-                else:
-                    self.tc_kwargs['run']['machine'] = args.machine
-                    return self._internal_run(logger, args)
-
-            else:
-                self.tc_kwargs['run']['machine'] = args.machine
-                rc = self._internal_run(logger, args)
+             rc = self._internal_run(logger, args)
         finally:
             config_paths = self.tc_kwargs['init']['config_paths']
 
diff --git a/poky/meta/lib/oeqa/utils/qemurunner.py b/poky/meta/lib/oeqa/utils/qemurunner.py
index a52fa41..29fe271 100644
--- a/poky/meta/lib/oeqa/utils/qemurunner.py
+++ b/poky/meta/lib/oeqa/utils/qemurunner.py
@@ -445,11 +445,9 @@
         self.logger.debug("Waiting at most %d seconds for login banner (%s)" %
                           (self.boottime, time.strftime("%D %H:%M:%S")))
         endtime = time.time() + self.boottime
-        newlinetime = time.time() + 120
         filelist = [self.server_socket, self.runqemu.stdout]
         reachedlogin = False
         stopread = False
-        sentnewlines = False
         qemusock = None
         bootlog = b''
         data = b''
@@ -458,16 +456,6 @@
                 sread, swrite, serror = select.select(filelist, [], [], 5)
             except InterruptedError:
                 continue
-            # With the 6.5 kernel, the serial port getty sometimes fails to appear, the data
-            # appears lost in some buffer somewhere. Wait two minutes, then if we've not had a login,
-            # try and provoke one. This is a workaround until we can work out the root cause.
-            if time.time() > newlinetime and not sentnewlines:
-                self.logger.warning('Probing the serial port to wake it up!')
-                try:
-                    self.server_socket.sendall(bytes("\n\n", "utf-8"))
-                    sentnewlines = True
-                except BrokenPipeError as e:
-                    self.logger.debug('Probe failed %s' % repr(e))
             for file in sread:
                 if file is self.server_socket:
                     qemusock, addr = self.server_socket.accept()
diff --git a/poky/meta/lib/patchtest/data.py b/poky/meta/lib/patchtest/data.py
new file mode 100644
index 0000000..3562599
--- /dev/null
+++ b/poky/meta/lib/patchtest/data.py
@@ -0,0 +1,86 @@
+# ex:ts=4:sw=4:sts=4:et
+# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
+#
+# patchtestdata: module used to share command line arguments between
+#                patchtest & test suite and a data store between test cases
+#
+# Copyright (C) 2016 Intel Corporation
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# NOTE: Strictly speaking, unit test should be isolated from outside,
+#       but patchtest test suites uses command line input data and
+#       pretest and test test cases may use the datastore defined
+#       on this module
+
+import os
+import argparse
+import collections
+import logging
+
+logger=logging.getLogger('patchtest')
+info=logger.info
+
+default_testdir = os.path.abspath(os.path.dirname(__file__) + "/tests")
+default_repodir = os.path.abspath(os.path.dirname(__file__) + "/../../..")
+
+# Data store commonly used to share values between pre and post-merge tests
+PatchTestDataStore = collections.defaultdict(str)
+
+class PatchTestInput(object):
+    """Abstract the patchtest argument parser"""
+
+    @classmethod
+    def set_namespace(cls):
+        parser = cls.get_parser()
+        parser.parse_args(namespace=cls)
+
+    @classmethod
+    def get_parser(cls):
+        parser = argparse.ArgumentParser()
+
+        target_patch_group = parser.add_mutually_exclusive_group(required=True)
+
+        target_patch_group.add_argument('--patch', metavar='PATCH', dest='patch_path',
+                            help='The patch to be tested')
+
+        target_patch_group.add_argument('--directory', metavar='DIRECTORY', dest='patch_path',
+                            help='The directory containing patches to be tested')
+
+        parser.add_argument('--repodir', metavar='REPO',
+                            default=default_repodir,
+                            help="Name of the repository where patch is merged")
+
+        parser.add_argument('--testdir', metavar='TESTDIR',
+                            default=default_testdir,
+                            help="Directory where test cases are located")
+
+        parser.add_argument('--top-level-directory', '-t',
+                            dest='topdir',
+                            default=None,
+                            help="Top level directory of project (defaults to start directory)")
+
+        parser.add_argument('--pattern', '-p',
+                            dest='pattern',
+                            default='test*.py',
+                            help="Pattern to match test files")
+
+        parser.add_argument('--base-branch', '-b',
+                            dest='basebranch',
+                            help="Branch name used by patchtest to branch from. By default, it uses the current one.")
+
+        parser.add_argument('--base-commit', '-c',
+                            dest='basecommit',
+                            help="Commit ID used by patchtest to branch from. By default, it uses HEAD.")
+
+        parser.add_argument('--debug', '-d',
+                            action='store_true',
+                            help='Enable debug output')
+
+        parser.add_argument('--log-results', 
+                            action='store_true', 
+                            help='Enable logging to a file matching the target patch name with ".testresult" appended')
+
+
+        return parser
+
diff --git a/poky/meta/lib/patchtest/patch.py b/poky/meta/lib/patchtest/patch.py
new file mode 100644
index 0000000..baf6283
--- /dev/null
+++ b/poky/meta/lib/patchtest/patch.py
@@ -0,0 +1,62 @@
+# ex:ts=4:sw=4:sts=4:et
+# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
+#
+# patchtestpatch: PatchTestPatch class which abstracts a patch file
+#
+# Copyright (C) 2016 Intel Corporation
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+
+import logging
+import utils
+
+logger = logging.getLogger('patchtest')
+
+class PatchTestPatch(object):
+    MERGE_STATUS_INVALID = 'INVALID'
+    MERGE_STATUS_NOT_MERGED = 'NOTMERGED'
+    MERGE_STATUS_MERGED_SUCCESSFULL = 'PASS'
+    MERGE_STATUS_MERGED_FAIL = 'FAIL'
+    MERGE_STATUS = (MERGE_STATUS_INVALID,
+                    MERGE_STATUS_NOT_MERGED,
+                    MERGE_STATUS_MERGED_SUCCESSFULL,
+                    MERGE_STATUS_MERGED_FAIL)
+
+    def __init__(self, path, forcereload=False):
+        self._path = path
+        self._forcereload = forcereload
+
+        self._contents = None
+        self._branch = None
+        self._merge_status = PatchTestPatch.MERGE_STATUS_NOT_MERGED
+
+    @property
+    def contents(self):
+        if self._forcereload or (not self._contents):
+            logger.debug('Reading %s contents' % self._path)
+            try:
+                with open(self._path, newline='') as _f:
+                    self._contents = _f.read()
+            except IOError:
+                logger.warn("Reading the mbox %s failed" % self.resource)
+        return self._contents
+
+    @property
+    def path(self):
+        return self._path
+
+    @property
+    def branch(self):
+        if not self._branch:
+            self._branch = utils.get_branch(self._path)
+        return self._branch
+
+    def setmergestatus(self, status):
+        self._merge_status = status
+
+    def getmergestatus(self):
+        return self._merge_status
+
+    merge_status = property(getmergestatus, setmergestatus)
+
diff --git a/poky/meta/lib/patchtest/repo.py b/poky/meta/lib/patchtest/repo.py
new file mode 100644
index 0000000..d3788f4
--- /dev/null
+++ b/poky/meta/lib/patchtest/repo.py
@@ -0,0 +1,174 @@
+# ex:ts=4:sw=4:sts=4:et
+# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
+#
+# patchtestrepo: PatchTestRepo class used mainly to control a git repo from patchtest
+#
+# Copyright (C) 2016 Intel Corporation
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+
+import os
+import utils
+import logging
+from patch import PatchTestPatch
+
+logger = logging.getLogger('patchtest')
+info=logger.info
+
+class PatchTestRepo(object):
+
+    # prefixes used for temporal branches/stashes
+    prefix = 'patchtest'
+
+    def __init__(self, patch, repodir, commit=None, branch=None):
+        self._repodir = repodir
+        self._patch = PatchTestPatch(patch)
+        self._current_branch = self._get_current_branch()
+
+        # targeted branch defined on the patch may be invalid, so make sure there
+        # is a corresponding remote branch
+        valid_patch_branch = None
+        if self._patch.branch in self.upstream_branches():
+            valid_patch_branch = self._patch.branch
+            
+        # Target Branch
+        # Priority (top has highest priority):
+        #    1. branch given at cmd line
+        #    2. branch given at the patch
+        #    3. current branch
+        self._branch = branch or valid_patch_branch or self._current_branch
+
+        # Target Commit
+        # Priority (top has highest priority):
+        #    1. commit given at cmd line
+        #    2. branch given at cmd line
+        #    3. branch given at the patch
+        #    3. current HEAD
+        self._commit = self._get_commitid(commit) or \
+          self._get_commitid(branch) or \
+          self._get_commitid(valid_patch_branch) or \
+          self._get_commitid('HEAD')
+
+        self._workingbranch = "%s_%s" % (PatchTestRepo.prefix, os.getpid())
+
+        # create working branch
+        self._exec({'cmd': ['git', 'checkout', '-b', self._workingbranch, self._commit]})
+
+        self._patchmerged = False
+
+        # Check if patch can be merged using git-am
+        self._patchcanbemerged = True
+        try:
+            self._exec({'cmd': ['git', 'am', '--keep-cr'], 'input': self._patch.contents})
+        except utils.CmdException as ce:
+            self._exec({'cmd': ['git', 'am', '--abort']})
+            self._patchcanbemerged = False
+        finally:
+            # if patch was applied, remove it
+            if self._patchcanbemerged:
+                self._exec({'cmd':['git', 'reset', '--hard', self._commit]})
+
+        # for debugging purposes, print all repo parameters
+        logger.debug("Parameters")
+        logger.debug("\tRepository     : %s" % self._repodir)
+        logger.debug("\tTarget Commit    : %s" % self._commit)
+        logger.debug("\tTarget Branch    : %s" % self._branch)
+        logger.debug("\tWorking branch : %s" % self._workingbranch)
+        logger.debug("\tPatch          : %s" % self._patch)
+
+    @property
+    def patch(self):
+        return self._patch.path
+
+    @property
+    def branch(self):
+        return self._branch
+
+    @property
+    def commit(self):
+        return self._commit
+
+    @property
+    def ismerged(self):
+        return self._patchmerged
+
+    @property
+    def canbemerged(self):
+        return self._patchcanbemerged
+
+    def _exec(self, cmds):
+        _cmds = []
+        if isinstance(cmds, dict):
+            _cmds.append(cmds)
+        elif isinstance(cmds, list):
+            _cmds = cmds
+        else:
+            raise utils.CmdException({'cmd':str(cmds)})
+
+        results = []
+        cmdfailure = False
+        try:
+            results = utils.exec_cmds(_cmds, self._repodir)
+        except utils.CmdException as ce:
+            cmdfailure = True
+            raise ce
+        finally:
+            if cmdfailure:
+                for cmd in _cmds:
+                    logger.debug("CMD: %s" % ' '.join(cmd['cmd']))
+            else:
+                for result in results:
+                    cmd, rc, stdout, stderr = ' '.join(result['cmd']), result['returncode'], result['stdout'], result['stderr']
+                    logger.debug("CMD: %s RCODE: %s STDOUT: %s STDERR: %s" % (cmd, rc, stdout, stderr))
+
+        return results
+
+    def _get_current_branch(self, commit='HEAD'):
+        cmd = {'cmd':['git', 'rev-parse', '--abbrev-ref', commit]}
+        cb = self._exec(cmd)[0]['stdout']
+        if cb == commit:
+            logger.warning('You may be detached so patchtest will checkout to master after execution')
+            cb = 'master'
+        return cb
+
+    def _get_commitid(self, commit):
+
+        if not commit:
+            return None
+
+        try:
+            cmd = {'cmd':['git', 'rev-parse', '--short', commit]}
+            return self._exec(cmd)[0]['stdout']
+        except utils.CmdException as ce:
+            # try getting the commit under any remotes
+            cmd = {'cmd':['git', 'remote']}
+            remotes = self._exec(cmd)[0]['stdout']
+            for remote in remotes.splitlines():
+                cmd = {'cmd':['git', 'rev-parse', '--short', '%s/%s' % (remote, commit)]}
+                try:
+                    return self._exec(cmd)[0]['stdout']
+                except utils.CmdException:
+                    pass
+
+        return None
+
+    def upstream_branches(self):
+        cmd = {'cmd':['git', 'branch', '--remotes']}
+        remote_branches = self._exec(cmd)[0]['stdout']
+
+        # just get the names, without the remote name
+        branches = set(branch.split('/')[-1] for branch in remote_branches.splitlines())
+        return branches
+
+    def merge(self):
+        if self._patchcanbemerged:
+            self._exec({'cmd': ['git', 'am', '--keep-cr'],
+                        'input': self._patch.contents,
+                        'updateenv': {'PTRESOURCE':self._patch.path}})
+            self._patchmerged = True
+
+    def clean(self):
+        self._exec({'cmd':['git', 'checkout', '%s' % self._current_branch]})
+        self._exec({'cmd':['git', 'branch', '-D', self._workingbranch]})
+        self._patchmerged = False
diff --git a/poky/meta/lib/patchtest/requirements.txt b/poky/meta/lib/patchtest/requirements.txt
index 785aa46..ba55ff9 100644
--- a/poky/meta/lib/patchtest/requirements.txt
+++ b/poky/meta/lib/patchtest/requirements.txt
@@ -1,3 +1,5 @@
+boto3
+git-pw>=2.5.0
 jinja2
 pylint
 pyparsing>=3.0.9
diff --git a/poky/meta/lib/patchtest/selftest/files/CVE.test_cve_presence_in_commit_message.fail b/poky/meta/lib/patchtest/selftest/files/CVE.test_cve_presence_in_commit_message.fail
deleted file mode 100644
index d40b8a9..0000000
--- a/poky/meta/lib/patchtest/selftest/files/CVE.test_cve_presence_in_commit_message.fail
+++ /dev/null
@@ -1,72 +0,0 @@
-From 14d72f6973270f78455a8628143f2cff90e8f41e Mon Sep 17 00:00:00 2001
-From: Trevor Gamblin <tgamblin@baylibre.com>
-Date: Tue, 29 Aug 2023 14:12:27 -0400
-Subject: [PATCH] selftest-hello: fix CVE-1234-56789
-
-This patch should fail the test for CVE presence in the mbox commit message.
-
-Signed-off-by: Trevor Gamblin <tgamblin@baylibre.com>
----
- .../selftest-hello/files/CVE-1234-56789.patch | 27 +++++++++++++++++++
- .../selftest-hello/selftest-hello_1.0.bb      |  6 +++--
- 2 files changed, 31 insertions(+), 2 deletions(-)
- create mode 100644 meta-selftest/recipes-test/selftest-hello/files/CVE-1234-56789.patch
-
-diff --git a/meta-selftest/recipes-test/selftest-hello/files/CVE-1234-56789.patch b/meta-selftest/recipes-test/selftest-hello/files/CVE-1234-56789.patch
-new file mode 100644
-index 0000000000..869cfb6fe5
---- /dev/null
-+++ b/meta-selftest/recipes-test/selftest-hello/files/CVE-1234-56789.patch
-@@ -0,0 +1,27 @@
-+From b26a31186e6ee2eb1f506d5f2f9394d327a0df2f Mon Sep 17 00:00:00 2001
-+From: Trevor Gamblin <tgamblin@baylibre.com>
-+Date: Tue, 29 Aug 2023 14:08:20 -0400
-+Subject: [PATCH] Fix CVE-NOT-REAL
-+
-+CVE: CVE-1234-56789
-+Upstream-Status: Backport(http://example.com/example)
-+
-+Signed-off-by: Trevor Gamblin <tgamblin@baylibre.com>
-+---
-+ strlen.c | 1 +
-+ 1 file changed, 1 insertion(+)
-+
-+diff --git a/strlen.c b/strlen.c
-+index 1788f38..83d7918 100644
-+--- a/strlen.c
-++++ b/strlen.c
-+@@ -8,6 +8,7 @@ int main() {
-+ 
-+ 	printf("%d\n", str_len(string1));
-+ 	printf("%d\n", str_len(string2));
-++	printf("CVE FIXED!!!\n");
-+ 
-+ 	return 0;
-+ }
-+-- 
-+2.41.0
-diff --git a/meta-selftest/recipes-test/selftest-hello/selftest-hello_1.0.bb b/meta-selftest/recipes-test/selftest-hello/selftest-hello_1.0.bb
-index 547587bef4..76975a6729 100644
---- a/meta-selftest/recipes-test/selftest-hello/selftest-hello_1.0.bb
-+++ b/meta-selftest/recipes-test/selftest-hello/selftest-hello_1.0.bb
-@@ -3,7 +3,9 @@ SECTION = "examples"
- LICENSE = "MIT"
- LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
- 
--SRC_URI = "file://helloworld.c"
-+SRC_URI = "file://helloworld.c \
-+           file://CVE-1234-56789.patch \
-+           "
- 
- S = "${WORKDIR}"
- 
-@@ -16,4 +18,4 @@ do_install() {
- 	install -m 0755 helloworld ${D}${bindir}
- }
- 
--BBCLASSEXTEND = "native nativesdk"
-\ No newline at end of file
-+BBCLASSEXTEND = "native nativesdk"
--- 
-2.41.0
-
diff --git a/poky/meta/lib/patchtest/selftest/files/CVE.test_cve_presence_in_commit_message.pass b/poky/meta/lib/patchtest/selftest/files/CVE.test_cve_presence_in_commit_message.pass
deleted file mode 100644
index 433c7a4..0000000
--- a/poky/meta/lib/patchtest/selftest/files/CVE.test_cve_presence_in_commit_message.pass
+++ /dev/null
@@ -1,74 +0,0 @@
-From 14d72f6973270f78455a8628143f2cff90e8f41e Mon Sep 17 00:00:00 2001
-From: Trevor Gamblin <tgamblin@baylibre.com>
-Date: Tue, 29 Aug 2023 14:12:27 -0400
-Subject: [PATCH] selftest-hello: fix CVE-1234-56789
-
-This test should pass the mbox cve tag test.
-
-CVE: CVE-1234-56789
-
-Signed-off-by: Trevor Gamblin <tgamblin@baylibre.com>
----
- .../selftest-hello/files/CVE-1234-56789.patch | 27 +++++++++++++++++++
- .../selftest-hello/selftest-hello_1.0.bb      |  6 +++--
- 2 files changed, 31 insertions(+), 2 deletions(-)
- create mode 100644 meta-selftest/recipes-test/selftest-hello/files/CVE-1234-56789.patch
-
-diff --git a/meta-selftest/recipes-test/selftest-hello/files/CVE-1234-56789.patch b/meta-selftest/recipes-test/selftest-hello/files/CVE-1234-56789.patch
-new file mode 100644
-index 0000000000..869cfb6fe5
---- /dev/null
-+++ b/meta-selftest/recipes-test/selftest-hello/files/CVE-1234-56789.patch
-@@ -0,0 +1,27 @@
-+From b26a31186e6ee2eb1f506d5f2f9394d327a0df2f Mon Sep 17 00:00:00 2001
-+From: Trevor Gamblin <tgamblin@baylibre.com>
-+Date: Tue, 29 Aug 2023 14:08:20 -0400
-+Subject: [PATCH] Fix CVE-NOT-REAL
-+
-+CVE: CVE-1234-56789
-+Upstream-Status: Backport(http://example.com/example)
-+
-+Signed-off-by: Trevor Gamblin <tgamblin@baylibre.com>
-+---
-+ strlen.c | 1 +
-+ 1 file changed, 1 insertion(+)
-+
-+diff --git a/strlen.c b/strlen.c
-+index 1788f38..83d7918 100644
-+--- a/strlen.c
-++++ b/strlen.c
-+@@ -8,6 +8,7 @@ int main() {
-+ 
-+ 	printf("%d\n", str_len(string1));
-+ 	printf("%d\n", str_len(string2));
-++	printf("CVE FIXED!!!\n");
-+ 
-+ 	return 0;
-+ }
-+-- 
-+2.41.0
-diff --git a/meta-selftest/recipes-test/selftest-hello/selftest-hello_1.0.bb b/meta-selftest/recipes-test/selftest-hello/selftest-hello_1.0.bb
-index 547587bef4..76975a6729 100644
---- a/meta-selftest/recipes-test/selftest-hello/selftest-hello_1.0.bb
-+++ b/meta-selftest/recipes-test/selftest-hello/selftest-hello_1.0.bb
-@@ -3,7 +3,9 @@ SECTION = "examples"
- LICENSE = "MIT"
- LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
- 
--SRC_URI = "file://helloworld.c"
-+SRC_URI = "file://helloworld.c \
-+           file://CVE-1234-56789.patch \
-+           "
- 
- S = "${WORKDIR}"
- 
-@@ -16,4 +18,4 @@ do_install() {
- 	install -m 0755 helloworld ${D}${bindir}
- }
- 
--BBCLASSEXTEND = "native nativesdk"
-\ No newline at end of file
-+BBCLASSEXTEND = "native nativesdk"
--- 
-2.41.0
-
diff --git a/poky/meta/lib/patchtest/selftest/files/PatchSignedOffBy.test_signed_off_by_presence.pass b/poky/meta/lib/patchtest/selftest/files/PatchSignedOffBy.test_signed_off_by_presence.pass
deleted file mode 100644
index ea34c76..0000000
--- a/poky/meta/lib/patchtest/selftest/files/PatchSignedOffBy.test_signed_off_by_presence.pass
+++ /dev/null
@@ -1,72 +0,0 @@
-From 14d72f6973270f78455a8628143f2cff90e8f41e Mon Sep 17 00:00:00 2001
-From: Trevor Gamblin <tgamblin@baylibre.com>
-Date: Tue, 29 Aug 2023 14:12:27 -0400
-Subject: [PATCH] selftest-hello: fix CVE-1234-56789
-
-CVE: CVE-1234-56789
-
-Signed-off-by: Trevor Gamblin <tgamblin@baylibre.com>
----
- .../selftest-hello/files/CVE-1234-56789.patch | 27 +++++++++++++++++++
- .../selftest-hello/selftest-hello_1.0.bb      |  6 +++--
- 2 files changed, 31 insertions(+), 2 deletions(-)
- create mode 100644 meta-selftest/recipes-test/selftest-hello/files/CVE-1234-56789.patch
-
-diff --git a/meta-selftest/recipes-test/selftest-hello/files/CVE-1234-56789.patch b/meta-selftest/recipes-test/selftest-hello/files/CVE-1234-56789.patch
-new file mode 100644
-index 0000000000..869cfb6fe5
---- /dev/null
-+++ b/meta-selftest/recipes-test/selftest-hello/files/CVE-1234-56789.patch
-@@ -0,0 +1,27 @@
-+From b26a31186e6ee2eb1f506d5f2f9394d327a0df2f Mon Sep 17 00:00:00 2001
-+From: Trevor Gamblin <tgamblin@baylibre.com>
-+Date: Tue, 29 Aug 2023 14:08:20 -0400
-+Subject: [PATCH] Fix CVE-NOT-REAL
-+
-+CVE: CVE-1234-56789
-+Upstream-Status: Backport(http://example.com/example)
-+
-+Signed-off-by: Trevor Gamblin <tgamblin@baylibre.com>
-+---
-+ strlen.c | 1 +
-+ 1 file changed, 1 insertion(+)
-+
-+diff --git a/strlen.c b/strlen.c
-+index 1788f38..83d7918 100644
-+--- a/strlen.c
-++++ b/strlen.c
-+@@ -8,6 +8,7 @@ int main() {
-+ 
-+ 	printf("%d\n", str_len(string1));
-+ 	printf("%d\n", str_len(string2));
-++	printf("CVE FIXED!!!\n");
-+ 
-+ 	return 0;
-+ }
-+-- 
-+2.41.0
-diff --git a/meta-selftest/recipes-test/selftest-hello/selftest-hello_1.0.bb b/meta-selftest/recipes-test/selftest-hello/selftest-hello_1.0.bb
-index 547587bef4..76975a6729 100644
---- a/meta-selftest/recipes-test/selftest-hello/selftest-hello_1.0.bb
-+++ b/meta-selftest/recipes-test/selftest-hello/selftest-hello_1.0.bb
-@@ -3,7 +3,9 @@ SECTION = "examples"
- LICENSE = "MIT"
- LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
- 
--SRC_URI = "file://helloworld.c"
-+SRC_URI = "file://helloworld.c \
-+           file://CVE-1234-56789.patch \
-+           "
- 
- S = "${WORKDIR}"
- 
-@@ -16,4 +18,4 @@ do_install() {
- 	install -m 0755 helloworld ${D}${bindir}
- }
- 
--BBCLASSEXTEND = "native nativesdk"
-\ No newline at end of file
-+BBCLASSEXTEND = "native nativesdk"
--- 
-2.41.0
-
diff --git a/poky/meta/lib/patchtest/selftest/files/Author.test_author_valid.1.fail b/poky/meta/lib/patchtest/selftest/files/TestMbox.test_author_valid.1.fail
similarity index 100%
rename from poky/meta/lib/patchtest/selftest/files/Author.test_author_valid.1.fail
rename to poky/meta/lib/patchtest/selftest/files/TestMbox.test_author_valid.1.fail
diff --git a/poky/meta/lib/patchtest/selftest/files/Author.test_author_valid.1.pass b/poky/meta/lib/patchtest/selftest/files/TestMbox.test_author_valid.1.pass
similarity index 100%
rename from poky/meta/lib/patchtest/selftest/files/Author.test_author_valid.1.pass
rename to poky/meta/lib/patchtest/selftest/files/TestMbox.test_author_valid.1.pass
diff --git a/poky/meta/lib/patchtest/selftest/files/Author.test_author_valid.2.fail b/poky/meta/lib/patchtest/selftest/files/TestMbox.test_author_valid.2.fail
similarity index 100%
rename from poky/meta/lib/patchtest/selftest/files/Author.test_author_valid.2.fail
rename to poky/meta/lib/patchtest/selftest/files/TestMbox.test_author_valid.2.fail
diff --git a/poky/meta/lib/patchtest/selftest/files/Author.test_author_valid.2.pass b/poky/meta/lib/patchtest/selftest/files/TestMbox.test_author_valid.2.pass
similarity index 100%
rename from poky/meta/lib/patchtest/selftest/files/Author.test_author_valid.2.pass
rename to poky/meta/lib/patchtest/selftest/files/TestMbox.test_author_valid.2.pass
diff --git a/poky/meta/lib/patchtest/selftest/files/Bugzilla.test_bugzilla_entry_format.fail b/poky/meta/lib/patchtest/selftest/files/TestMbox.test_bugzilla_entry_format.fail
similarity index 100%
rename from poky/meta/lib/patchtest/selftest/files/Bugzilla.test_bugzilla_entry_format.fail
rename to poky/meta/lib/patchtest/selftest/files/TestMbox.test_bugzilla_entry_format.fail
diff --git a/poky/meta/lib/patchtest/selftest/files/Bugzilla.test_bugzilla_entry_format.pass b/poky/meta/lib/patchtest/selftest/files/TestMbox.test_bugzilla_entry_format.pass
similarity index 100%
rename from poky/meta/lib/patchtest/selftest/files/Bugzilla.test_bugzilla_entry_format.pass
rename to poky/meta/lib/patchtest/selftest/files/TestMbox.test_bugzilla_entry_format.pass
diff --git a/poky/meta/lib/patchtest/selftest/files/CommitMessage.test_commit_message_presence.fail b/poky/meta/lib/patchtest/selftest/files/TestMbox.test_commit_message_presence.fail
similarity index 100%
rename from poky/meta/lib/patchtest/selftest/files/CommitMessage.test_commit_message_presence.fail
rename to poky/meta/lib/patchtest/selftest/files/TestMbox.test_commit_message_presence.fail
diff --git a/poky/meta/lib/patchtest/selftest/files/CommitMessage.test_commit_message_presence.pass b/poky/meta/lib/patchtest/selftest/files/TestMbox.test_commit_message_presence.pass
similarity index 100%
rename from poky/meta/lib/patchtest/selftest/files/CommitMessage.test_commit_message_presence.pass
rename to poky/meta/lib/patchtest/selftest/files/TestMbox.test_commit_message_presence.pass
diff --git a/poky/meta/lib/patchtest/selftest/files/MboxFormat.test_mbox_format.1.fail b/poky/meta/lib/patchtest/selftest/files/TestMbox.test_mbox_format.1.fail
similarity index 100%
rename from poky/meta/lib/patchtest/selftest/files/MboxFormat.test_mbox_format.1.fail
rename to poky/meta/lib/patchtest/selftest/files/TestMbox.test_mbox_format.1.fail
diff --git a/poky/meta/lib/patchtest/selftest/files/MboxFormat.test_mbox_format.2.fail b/poky/meta/lib/patchtest/selftest/files/TestMbox.test_mbox_format.2.fail
similarity index 100%
rename from poky/meta/lib/patchtest/selftest/files/MboxFormat.test_mbox_format.2.fail
rename to poky/meta/lib/patchtest/selftest/files/TestMbox.test_mbox_format.2.fail
diff --git a/poky/meta/lib/patchtest/selftest/files/MboxFormat.test_mbox_format.pass b/poky/meta/lib/patchtest/selftest/files/TestMbox.test_mbox_format.pass
similarity index 100%
rename from poky/meta/lib/patchtest/selftest/files/MboxFormat.test_mbox_format.pass
rename to poky/meta/lib/patchtest/selftest/files/TestMbox.test_mbox_format.pass
diff --git a/poky/meta/lib/patchtest/selftest/files/Merge.test_series_merge_on_head.pass b/poky/meta/lib/patchtest/selftest/files/TestMbox.test_series_merge_on_head.1.skip
similarity index 100%
rename from poky/meta/lib/patchtest/selftest/files/Merge.test_series_merge_on_head.pass
rename to poky/meta/lib/patchtest/selftest/files/TestMbox.test_series_merge_on_head.1.skip
diff --git a/poky/meta/lib/patchtest/selftest/files/Merge.test_series_merge_on_head.fail b/poky/meta/lib/patchtest/selftest/files/TestMbox.test_series_merge_on_head.2.skip
similarity index 100%
rename from poky/meta/lib/patchtest/selftest/files/Merge.test_series_merge_on_head.fail
rename to poky/meta/lib/patchtest/selftest/files/TestMbox.test_series_merge_on_head.2.skip
diff --git a/poky/meta/lib/patchtest/selftest/files/Shortlog.test_shortlog_format.fail b/poky/meta/lib/patchtest/selftest/files/TestMbox.test_shortlog_format.fail
similarity index 100%
rename from poky/meta/lib/patchtest/selftest/files/Shortlog.test_shortlog_format.fail
rename to poky/meta/lib/patchtest/selftest/files/TestMbox.test_shortlog_format.fail
diff --git a/poky/meta/lib/patchtest/selftest/files/Shortlog.test_shortlog_format.pass b/poky/meta/lib/patchtest/selftest/files/TestMbox.test_shortlog_format.pass
similarity index 100%
rename from poky/meta/lib/patchtest/selftest/files/Shortlog.test_shortlog_format.pass
rename to poky/meta/lib/patchtest/selftest/files/TestMbox.test_shortlog_format.pass
diff --git a/poky/meta/lib/patchtest/selftest/files/Shortlog.test_shortlog_length.fail b/poky/meta/lib/patchtest/selftest/files/TestMbox.test_shortlog_length.fail
similarity index 97%
rename from poky/meta/lib/patchtest/selftest/files/Shortlog.test_shortlog_length.fail
rename to poky/meta/lib/patchtest/selftest/files/TestMbox.test_shortlog_length.fail
index 247b2a8..629e785 100644
--- a/poky/meta/lib/patchtest/selftest/files/Shortlog.test_shortlog_length.fail
+++ b/poky/meta/lib/patchtest/selftest/files/TestMbox.test_shortlog_length.fail
@@ -56,7 +56,7 @@
  
 -SRC_URI = "file://helloworld.c"
 +SRC_URI = "file://helloworld.c \
-+           file://CVE-1234-56789.patch \
++           file://0001-Fix-CVE-1234-56789.patch \
 +           "
  
  S = "${WORKDIR}"
diff --git a/poky/meta/lib/patchtest/selftest/files/Shortlog.test_shortlog_length.pass b/poky/meta/lib/patchtest/selftest/files/TestMbox.test_shortlog_length.pass
similarity index 100%
rename from poky/meta/lib/patchtest/selftest/files/Shortlog.test_shortlog_length.pass
rename to poky/meta/lib/patchtest/selftest/files/TestMbox.test_shortlog_length.pass
diff --git a/poky/meta/lib/patchtest/selftest/files/SignedOffBy.test_signed_off_by_presence.1.fail b/poky/meta/lib/patchtest/selftest/files/TestMbox.test_signed_off_by_presence.1.fail
similarity index 100%
rename from poky/meta/lib/patchtest/selftest/files/SignedOffBy.test_signed_off_by_presence.1.fail
rename to poky/meta/lib/patchtest/selftest/files/TestMbox.test_signed_off_by_presence.1.fail
diff --git a/poky/meta/lib/patchtest/selftest/files/SignedOffBy.test_signed_off_by_presence.2.fail b/poky/meta/lib/patchtest/selftest/files/TestMbox.test_signed_off_by_presence.2.fail
similarity index 100%
rename from poky/meta/lib/patchtest/selftest/files/SignedOffBy.test_signed_off_by_presence.2.fail
rename to poky/meta/lib/patchtest/selftest/files/TestMbox.test_signed_off_by_presence.2.fail
diff --git a/poky/meta/lib/patchtest/selftest/files/SignedOffBy.test_signed_off_by_presence.pass b/poky/meta/lib/patchtest/selftest/files/TestMbox.test_signed_off_by_presence.pass
similarity index 100%
rename from poky/meta/lib/patchtest/selftest/files/SignedOffBy.test_signed_off_by_presence.pass
rename to poky/meta/lib/patchtest/selftest/files/TestMbox.test_signed_off_by_presence.pass
diff --git a/poky/meta/lib/patchtest/selftest/files/LicFilesChkSum.test_lic_files_chksum_modified_not_mentioned.fail b/poky/meta/lib/patchtest/selftest/files/TestMetadata.test_lic_files_chksum_modified_not_mentioned.fail
similarity index 100%
rename from poky/meta/lib/patchtest/selftest/files/LicFilesChkSum.test_lic_files_chksum_modified_not_mentioned.fail
rename to poky/meta/lib/patchtest/selftest/files/TestMetadata.test_lic_files_chksum_modified_not_mentioned.fail
diff --git a/poky/meta/lib/patchtest/selftest/files/LicFilesChkSum.test_lic_files_chksum_modified_not_mentioned.pass b/poky/meta/lib/patchtest/selftest/files/TestMetadata.test_lic_files_chksum_modified_not_mentioned.pass
similarity index 100%
rename from poky/meta/lib/patchtest/selftest/files/LicFilesChkSum.test_lic_files_chksum_modified_not_mentioned.pass
rename to poky/meta/lib/patchtest/selftest/files/TestMetadata.test_lic_files_chksum_modified_not_mentioned.pass
diff --git a/poky/meta/lib/patchtest/selftest/files/LicFilesChkSum.test_lic_files_chksum_presence.fail b/poky/meta/lib/patchtest/selftest/files/TestMetadata.test_lic_files_chksum_presence.fail
similarity index 100%
rename from poky/meta/lib/patchtest/selftest/files/LicFilesChkSum.test_lic_files_chksum_presence.fail
rename to poky/meta/lib/patchtest/selftest/files/TestMetadata.test_lic_files_chksum_presence.fail
diff --git a/poky/meta/lib/patchtest/selftest/files/LicFilesChkSum.test_lic_files_chksum_presence.pass b/poky/meta/lib/patchtest/selftest/files/TestMetadata.test_lic_files_chksum_presence.pass
similarity index 100%
rename from poky/meta/lib/patchtest/selftest/files/LicFilesChkSum.test_lic_files_chksum_presence.pass
rename to poky/meta/lib/patchtest/selftest/files/TestMetadata.test_lic_files_chksum_presence.pass
diff --git a/poky/meta/lib/patchtest/selftest/files/SrcUri.test_src_uri_left_files.fail b/poky/meta/lib/patchtest/selftest/files/TestMetadata.test_src_uri_left_files.fail
similarity index 100%
rename from poky/meta/lib/patchtest/selftest/files/SrcUri.test_src_uri_left_files.fail
rename to poky/meta/lib/patchtest/selftest/files/TestMetadata.test_src_uri_left_files.fail
diff --git a/poky/meta/lib/patchtest/selftest/files/SrcUri.test_src_uri_left_files.pass b/poky/meta/lib/patchtest/selftest/files/TestMetadata.test_src_uri_left_files.pass
similarity index 100%
rename from poky/meta/lib/patchtest/selftest/files/SrcUri.test_src_uri_left_files.pass
rename to poky/meta/lib/patchtest/selftest/files/TestMetadata.test_src_uri_left_files.pass
diff --git a/poky/meta/lib/patchtest/selftest/files/Summary.test_summary_presence.fail b/poky/meta/lib/patchtest/selftest/files/TestMetadata.test_summary_presence.fail
similarity index 100%
rename from poky/meta/lib/patchtest/selftest/files/Summary.test_summary_presence.fail
rename to poky/meta/lib/patchtest/selftest/files/TestMetadata.test_summary_presence.fail
diff --git a/poky/meta/lib/patchtest/selftest/files/Summary.test_summary_presence.pass b/poky/meta/lib/patchtest/selftest/files/TestMetadata.test_summary_presence.pass
similarity index 100%
rename from poky/meta/lib/patchtest/selftest/files/Summary.test_summary_presence.pass
rename to poky/meta/lib/patchtest/selftest/files/TestMetadata.test_summary_presence.pass
diff --git a/poky/meta/lib/patchtest/selftest/files/CVE.test_cve_tag_format.fail b/poky/meta/lib/patchtest/selftest/files/TestPatch.test_cve_tag_format.fail
similarity index 100%
rename from poky/meta/lib/patchtest/selftest/files/CVE.test_cve_tag_format.fail
rename to poky/meta/lib/patchtest/selftest/files/TestPatch.test_cve_tag_format.fail
diff --git a/poky/meta/lib/patchtest/selftest/files/CVE.test_cve_tag_format.pass b/poky/meta/lib/patchtest/selftest/files/TestPatch.test_cve_tag_format.pass
similarity index 100%
rename from poky/meta/lib/patchtest/selftest/files/CVE.test_cve_tag_format.pass
rename to poky/meta/lib/patchtest/selftest/files/TestPatch.test_cve_tag_format.pass
diff --git a/poky/meta/lib/patchtest/selftest/files/PatchSignedOffBy.test_signed_off_by_presence.fail b/poky/meta/lib/patchtest/selftest/files/TestPatch.test_signed_off_by_presence.fail
similarity index 100%
rename from poky/meta/lib/patchtest/selftest/files/PatchSignedOffBy.test_signed_off_by_presence.fail
rename to poky/meta/lib/patchtest/selftest/files/TestPatch.test_signed_off_by_presence.fail
diff --git a/poky/meta/lib/patchtest/selftest/files/SignedOffBy.test_signed_off_by_presence.pass b/poky/meta/lib/patchtest/selftest/files/TestPatch.test_signed_off_by_presence.pass
similarity index 100%
copy from poky/meta/lib/patchtest/selftest/files/SignedOffBy.test_signed_off_by_presence.pass
copy to poky/meta/lib/patchtest/selftest/files/TestPatch.test_signed_off_by_presence.pass
diff --git a/poky/meta/lib/patchtest/selftest/selftest b/poky/meta/lib/patchtest/selftest/selftest
index d2b61e9..6fad50c 100755
--- a/poky/meta/lib/patchtest/selftest/selftest
+++ b/poky/meta/lib/patchtest/selftest/selftest
@@ -4,7 +4,7 @@
 #
 # Copyright (C) 2016 Intel Corporation
 #
-# SPDX-License-Identifier: GPL-2.0
+# SPDX-License-Identifier: GPL-2.0-only
 
 import os
 import subprocess
@@ -18,14 +18,15 @@
 # path to the repo root
 repodir = os.path.dirname(os.path.dirname(parentdir))
 
-def print_results(passcount, skipcount, failcount, xpasscount, xfailcount, errorcount):
-    total = passcount + skipcount + failcount + xpasscount + xfailcount + errorcount
+def print_results(passcount, failcount, skipcount, xpasscount, xfailcount, xskipcount, errorcount):
+    total = passcount + skipcount + failcount + xpasscount + xfailcount + xskipcount + errorcount
     print("============================================================================")
     print("Testsuite summary for %s" % os.path.basename(topdir))
     print("============================================================================")
     print("# TOTAL: %s" % str(total))
     print("# XPASS: %s" % str(xpasscount))
     print("# XFAIL: %s" % str(xfailcount))
+    print("# XSKIP: %s" % str(xskipcount))
     print("# PASS: %s" % str(passcount))
     print("# FAIL: %s" % str(failcount))
     print("# SKIP: %s" % str(skipcount))
@@ -37,7 +38,7 @@
     res = True
     patchpath = os.path.abspath(os.path.join(root, patch))
     
-    cmd     = 'patchtest %s %s/tests --patch %s' % (repodir, topdir, patchpath)
+    cmd     = 'patchtest --repodir %s --testdir %s/tests --patch %s' % (repodir, topdir, patchpath)
     results = subprocess.check_output(cmd, stderr=subprocess.STDOUT, universal_newlines=True, shell=True)
 
     return results
@@ -48,6 +49,7 @@
     skipcount = 0
     xpasscount = 0
     xfailcount = 0
+    xskipcount = 0
     errorcount = 0
 
     results = None
@@ -63,7 +65,7 @@
 
             for resultline in results.splitlines():
                 if testid in resultline:
-                    result, _ = resultline.split(' ', 1)
+                    result, _ = resultline.split(':', 1)
 
                     if expected_result.upper() == "FAIL" and result.upper() == "FAIL":
                         xfailcount = xfailcount + 1
@@ -71,6 +73,9 @@
                     elif expected_result.upper() == "PASS" and result.upper() == "PASS":
                         xpasscount = xpasscount + 1
                         print("XPASS: %s (file: %s)" % (testid.strip("."), os.path.basename(patch)))
+                    elif expected_result.upper() == "SKIP" and result.upper() == "SKIP":
+                        xskipcount = xskipcount + 1
+                        print("XSKIP: %s (file: %s)" % (testid.strip("."), os.path.basename(patch)))
                     else:
                         print("%s: %s (%s)" % (result.upper(), testid.strip("."), os.path.basename(patch)))
                         if result.upper() == "PASS":
@@ -86,4 +91,4 @@
             else:
                 print ("No test for=%s" % patch)
 
-    print_results(passcount, skipcount, failcount, xpasscount, xfailcount, errorcount)
+    print_results(passcount, failcount, skipcount, xpasscount, xfailcount, xskipcount, errorcount)
diff --git a/poky/meta/lib/patchtest/tests/base.py b/poky/meta/lib/patchtest/tests/base.py
index 27db380..aecbbc4 100644
--- a/poky/meta/lib/patchtest/tests/base.py
+++ b/poky/meta/lib/patchtest/tests/base.py
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 2016 Intel Corporation
 #
-# SPDX-License-Identifier: GPL-2.0
+# SPDX-License-Identifier: GPL-2.0-only
 
 import unittest
 import logging
diff --git a/poky/meta/lib/patchtest/tests/pyparsing/common.py b/poky/meta/lib/patchtest/tests/pyparsing/common.py
index 9d37b04..cbce4c3 100644
--- a/poky/meta/lib/patchtest/tests/pyparsing/common.py
+++ b/poky/meta/lib/patchtest/tests/pyparsing/common.py
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 2016 Intel Corporation
 #
-# SPDX-License-Identifier: GPL-2.0
+# SPDX-License-Identifier: GPL-2.0-only
 
 import pyparsing
 
diff --git a/poky/meta/lib/patchtest/tests/pyparsing/parse_cve_tags.py b/poky/meta/lib/patchtest/tests/pyparsing/parse_cve_tags.py
index dd7131a..f7fb82e 100644
--- a/poky/meta/lib/patchtest/tests/pyparsing/parse_cve_tags.py
+++ b/poky/meta/lib/patchtest/tests/pyparsing/parse_cve_tags.py
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 2016 Intel Corporation
 #
-# SPDX-License-Identifier: GPL-2.0
+# SPDX-License-Identifier: GPL-2.0-only
 
 
 import pyparsing
diff --git a/poky/meta/lib/patchtest/tests/pyparsing/parse_shortlog.py b/poky/meta/lib/patchtest/tests/pyparsing/parse_shortlog.py
index 26e9612..30d3ab3 100644
--- a/poky/meta/lib/patchtest/tests/pyparsing/parse_shortlog.py
+++ b/poky/meta/lib/patchtest/tests/pyparsing/parse_shortlog.py
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 2016 Intel Corporation
 #
-# SPDX-License-Identifier: GPL-2.0
+# SPDX-License-Identifier: GPL-2.0-only
 
 # NOTE:This is an oversimplified syntax of the mbox's summary
 
diff --git a/poky/meta/lib/patchtest/tests/pyparsing/parse_signed_off_by.py b/poky/meta/lib/patchtest/tests/pyparsing/parse_signed_off_by.py
index c8a4351..692ebec 100644
--- a/poky/meta/lib/patchtest/tests/pyparsing/parse_signed_off_by.py
+++ b/poky/meta/lib/patchtest/tests/pyparsing/parse_signed_off_by.py
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 2016 Intel Corporation
 #
-# SPDX-License-Identifier: GPL-2.0
+# SPDX-License-Identifier: GPL-2.0-only
 
 
 import pyparsing
diff --git a/poky/meta/lib/patchtest/tests/pyparsing/parse_upstream_status.py b/poky/meta/lib/patchtest/tests/pyparsing/parse_upstream_status.py
index d63567e..bc6c427 100644
--- a/poky/meta/lib/patchtest/tests/pyparsing/parse_upstream_status.py
+++ b/poky/meta/lib/patchtest/tests/pyparsing/parse_upstream_status.py
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 2016 Intel Corporation
 #
-# SPDX-License-Identifier: GPL-2.0
+# SPDX-License-Identifier: GPL-2.0-only
 
 
 import common
diff --git a/poky/meta/lib/patchtest/tests/test_mbox.py b/poky/meta/lib/patchtest/tests/test_mbox.py
new file mode 100644
index 0000000..0b623b7
--- /dev/null
+++ b/poky/meta/lib/patchtest/tests/test_mbox.py
@@ -0,0 +1,159 @@
+# Checks related to the patch's author
+#
+# Copyright (C) 2016 Intel Corporation
+#
+# SPDX-License-Identifier: GPL-2.0-only
+
+import base
+import collections
+import parse_shortlog
+import parse_signed_off_by
+import pyparsing
+import subprocess
+from data import PatchTestInput
+
+def headlog():
+    output = subprocess.check_output(
+        "cd %s; git log --pretty='%%h#%%aN#%%cD:#%%s' -1" % PatchTestInput.repodir,
+        universal_newlines=True,
+        shell=True
+        )
+    return output.split('#')
+
+class TestMbox(base.Base):
+
+    auh_email = 'auh@auh.yoctoproject.org'
+
+    invalids = [pyparsing.Regex("^Upgrade Helper.+"),
+                pyparsing.Regex(auh_email),
+                pyparsing.Regex("uh@not\.set"),
+                pyparsing.Regex("\S+@example\.com")]
+
+    rexp_detect = pyparsing.Regex('\[\s?YOCTO.*\]')
+    rexp_validation = pyparsing.Regex('\[(\s?YOCTO\s?#\s?(\d+)\s?,?)+\]')
+    revert_shortlog_regex = pyparsing.Regex('Revert\s+".*"')
+    signoff_prog = parse_signed_off_by.signed_off_by
+    revert_shortlog_regex = pyparsing.Regex('Revert\s+".*"')
+    maxlength = 90
+
+    # base paths of main yocto project sub-projects
+    paths = {
+        'oe-core': ['meta-selftest', 'meta-skeleton', 'meta', 'scripts'],
+        'bitbake': ['bitbake'],
+        'documentation': ['documentation'],
+        'poky': ['meta-poky','meta-yocto-bsp'],
+        'oe': ['meta-gpe', 'meta-gnome', 'meta-efl', 'meta-networking', 'meta-multimedia','meta-initramfs', 'meta-ruby', 'contrib', 'meta-xfce', 'meta-filesystems', 'meta-perl', 'meta-webserver', 'meta-systemd', 'meta-oe', 'meta-python']
+        }
+
+    # scripts folder is a mix of oe-core and poky, most is oe-core code except:
+    poky_scripts = ['scripts/yocto-bsp', 'scripts/yocto-kernel', 'scripts/yocto-layer', 'scripts/lib/bsp']
+
+    Project = collections.namedtuple('Project', ['name', 'listemail', 'gitrepo', 'paths'])
+
+    bitbake = Project(name='Bitbake', listemail='bitbake-devel@lists.openembedded.org', gitrepo='http://git.openembedded.org/bitbake/', paths=paths['bitbake'])
+    doc     = Project(name='Documentantion', listemail='yocto@yoctoproject.org', gitrepo='http://git.yoctoproject.org/cgit/cgit.cgi/yocto-docs/', paths=paths['documentation'])
+    poky    = Project(name='Poky', listemail='poky@yoctoproject.org', gitrepo='http://git.yoctoproject.org/cgit/cgit.cgi/poky/', paths=paths['poky'])
+    oe      = Project(name='oe', listemail='openembedded-devel@lists.openembedded.org', gitrepo='http://git.openembedded.org/meta-openembedded/', paths=paths['oe'])
+
+
+    def test_signed_off_by_presence(self):
+        for commit in TestMbox.commits:
+            # skip those patches that revert older commits, these do not required the tag presence
+            if self.revert_shortlog_regex.search_string(commit.shortlog):
+                continue
+            if not self.signoff_prog.search_string(commit.payload):
+                self.fail('Mbox is missing Signed-off-by. Add it manually or with "git commit --amend -s"',
+                          commit=commit)
+
+    def test_shortlog_format(self):
+        for commit in TestMbox.commits:
+            shortlog = commit.shortlog
+            if not shortlog.strip():
+                self.skip('Empty shortlog, no reason to execute shortlog format test')
+            else:
+                # no reason to re-check on revert shortlogs
+                if shortlog.startswith('Revert "'):
+                    continue
+                try:
+                    parse_shortlog.shortlog.parseString(shortlog)
+                except pyparsing.ParseException as pe:
+                    self.fail('Commit shortlog (first line of commit message) should follow the format "<target>: <summary>"',
+                              commit=commit)
+
+    def test_shortlog_length(self):
+        for commit in TestMbox.commits:
+            # no reason to re-check on revert shortlogs
+            shortlog = commit.shortlog
+            if shortlog.startswith('Revert "'):
+                continue
+            l = len(shortlog)
+            if l > self.maxlength:
+                self.fail('Edit shortlog so that it is %d characters or less (currently %d characters)' % (self.maxlength, l),
+                          commit=commit)
+
+    def test_series_merge_on_head(self):
+        self.skip("Merge test is disabled for now")
+        if PatchTestInput.repo.branch != "master":
+            self.skip("Skipping merge test since patch is not intended for master branch. Target detected is %s" % PatchTestInput.repo.branch)
+        if not PatchTestInput.repo.ismerged:
+            commithash, author, date, shortlog = headlog()
+            self.fail('Series does not apply on top of target branch %s' % PatchTestInput.repo.branch,
+                      data=[('Targeted branch', '%s (currently at %s)' % (PatchTestInput.repo.branch, commithash))])
+
+    def test_target_mailing_list(self):
+        """In case of merge failure, check for other targeted projects"""
+        if PatchTestInput.repo.ismerged:
+            self.skip('Series merged, no reason to check other mailing lists')
+
+        # a meta project may be indicted in the message subject, if this is the case, just fail
+        # TODO: there may be other project with no-meta prefix, we also need to detect these
+        project_regex = pyparsing.Regex("\[(?P<project>meta-.+)\]")
+        for commit in TestMbox.commits:
+            match = project_regex.search_string(commit.subject)
+            if match:
+                self.fail('Series sent to the wrong mailing list or some patches from the series correspond to different mailing lists',
+                          commit=commit)
+
+        for patch in self.patchset:
+            folders = patch.path.split('/')
+            base_path = folders[0]
+            for project in [self.bitbake, self.doc, self.oe, self.poky]:
+                if base_path in  project.paths:
+                    self.fail('Series sent to the wrong mailing list or some patches from the series correspond to different mailing lists',
+                              data=[('Suggested ML', '%s [%s]' % (project.listemail, project.gitrepo)),
+                                    ('Patch\'s path:', patch.path)])
+
+            # check for poky's scripts code
+            if base_path.startswith('scripts'):
+                for poky_file in self.poky_scripts:
+                    if patch.path.startswith(poky_file):
+                        self.fail('Series sent to the wrong mailing list or some patches from the series correspond to different mailing lists',
+                                  data=[('Suggested ML', '%s [%s]' % (self.poky.listemail, self.poky.gitrepo)),('Patch\'s path:', patch.path)])
+
+    def test_mbox_format(self):
+        if self.unidiff_parse_error:
+            self.fail('Series has malformed diff lines. Create the series again using git-format-patch and ensure it applies using git am',
+                      data=[('Diff line',self.unidiff_parse_error)])
+
+    def test_commit_message_presence(self):
+        for commit in TestMbox.commits:
+            if not commit.commit_message.strip():
+                self.fail('Please include a commit message on your patch explaining the change', commit=commit)
+
+    def test_bugzilla_entry_format(self):
+        for commit in TestMbox.commits:
+            if not self.rexp_detect.search_string(commit.commit_message):
+                self.skip("No bug ID found")
+            elif not self.rexp_validation.search_string(commit.commit_message):
+                self.fail('Bugzilla issue ID is not correctly formatted - specify it with format: "[YOCTO #<bugzilla ID>]"', commit=commit)
+
+    def test_author_valid(self):
+        for commit in self.commits:
+            for invalid in self.invalids:
+                if invalid.search_string(commit.author):
+                    self.fail('Invalid author %s. Resend the series with a valid patch author' % commit.author, commit=commit)
+
+    def test_non_auh_upgrade(self):
+        for commit in self.commits:
+            if self.auh_email in commit.payload:
+                self.fail('Invalid author %s. Resend the series with a valid patch author' % self.auh_email, commit=commit)
diff --git a/poky/meta/lib/patchtest/tests/test_mbox_author.py b/poky/meta/lib/patchtest/tests/test_mbox_author.py
deleted file mode 100644
index 6c79f16..0000000
--- a/poky/meta/lib/patchtest/tests/test_mbox_author.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# Checks related to the patch's author
-#
-# Copyright (C) 2016 Intel Corporation
-#
-# SPDX-License-Identifier: GPL-2.0
-
-import base
-import re
-
-class Author(base.Base):
-
-    auh_email = '<auh@auh.yoctoproject.org>'
-
-    invalids = [re.compile("^Upgrade Helper.+"),
-                re.compile(re.escape(auh_email)),
-                re.compile("uh@not\.set"),
-                re.compile("\S+@example\.com")]
-
-
-    def test_author_valid(self):
-        for commit in self.commits:
-            for invalid in self.invalids:
-                if invalid.search(commit.author):
-                    self.fail('Invalid author %s' % commit.author, 'Resend the series with a valid patch\'s author', commit)
-
-    def test_non_auh_upgrade(self):
-        for commit in self.commits:
-            if self.auh_email in commit.payload:
-                self.fail('Invalid author %s in commit message' % self.auh_email, 'Resend the series with a valid patch\'s author', commit)
diff --git a/poky/meta/lib/patchtest/tests/test_mbox_bugzilla.py b/poky/meta/lib/patchtest/tests/test_mbox_bugzilla.py
deleted file mode 100644
index e8de48b..0000000
--- a/poky/meta/lib/patchtest/tests/test_mbox_bugzilla.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# Checks related to the patch's bugzilla tag
-#
-# Copyright (C) 2016 Intel Corporation
-#
-# SPDX-License-Identifier: GPL-2.0
-
-import re
-import base
-
-class Bugzilla(base.Base):
-    rexp_detect     = re.compile("\[\s?YOCTO.*\]", re.IGNORECASE)
-    rexp_validation = re.compile("\[(\s?YOCTO\s?#\s?(\d+)\s?,?)+\]", re.IGNORECASE)
-
-    def test_bugzilla_entry_format(self):
-        for commit in Bugzilla.commits:
-            for line in commit.commit_message.splitlines():
-                if self.rexp_detect.match(line):
-                    if not self.rexp_validation.match(line):
-                        self.fail('Yocto Project bugzilla tag is not correctly formatted',
-                                  'Specify bugzilla ID in commit description with format: "[YOCTO #<bugzilla ID>]"',
-                                  commit)
-
diff --git a/poky/meta/lib/patchtest/tests/test_mbox_cve.py b/poky/meta/lib/patchtest/tests/test_mbox_cve.py
deleted file mode 100644
index f99194c..0000000
--- a/poky/meta/lib/patchtest/tests/test_mbox_cve.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# Checks related to the patch's CVE lines
-#
-# Copyright (C) 2016 Intel Corporation
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-# SPDX-License-Identifier: GPL-2.0-or-later
-
-import base
-import os
-import parse_cve_tags
-import re
-
-class CVE(base.Base):
-
-    revert_shortlog_regex = re.compile('Revert\s+".*"')
-    prog = parse_cve_tags.cve_tag
-
-    def setUp(self):
-        if self.unidiff_parse_error:
-            self.skip('Parse error %s' % self.unidiff_parse_error)
-
-        # we are just interested in series that introduce CVE patches, thus discard other
-        # possibilities: modification to current CVEs, patch directly introduced into the
-        # recipe, upgrades already including the CVE, etc.
-        new_cves = [p for p in self.patchset if p.path.endswith('.patch') and p.is_added_file]
-        if not new_cves:
-            self.skip('No new CVE patches introduced')
-
-    def test_cve_presence_in_commit_message(self):
-        for commit in CVE.commits:
-            # skip those patches that revert older commits, these do not required the tag presence
-            if self.revert_shortlog_regex.match(commit.shortlog):
-                continue
-            if not self.prog.search_string(commit.payload):
-                self.fail('Missing or incorrectly formatted CVE tag in mbox',
-                          'Correct or include the CVE tag in the mbox with format: "CVE: CVE-YYYY-XXXX"',
-                          commit)
diff --git a/poky/meta/lib/patchtest/tests/test_mbox_description.py b/poky/meta/lib/patchtest/tests/test_mbox_description.py
deleted file mode 100644
index 7addc6b..0000000
--- a/poky/meta/lib/patchtest/tests/test_mbox_description.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Checks related to the patch's commit_message
-#
-# Copyright (C) 2016 Intel Corporation
-#
-# SPDX-License-Identifier: GPL-2.0
-
-import base
-
-class CommitMessage(base.Base):
-
-    def test_commit_message_presence(self):
-        for commit in CommitMessage.commits:
-            if not commit.commit_message.strip():
-                self.fail('Patch is missing a descriptive commit message',
-                          'Please include a commit message on your patch explaining the change (most importantly why the change is being made)',
-                          commit)
-
diff --git a/poky/meta/lib/patchtest/tests/test_mbox_format.py b/poky/meta/lib/patchtest/tests/test_mbox_format.py
deleted file mode 100644
index 85c452c..0000000
--- a/poky/meta/lib/patchtest/tests/test_mbox_format.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Checks correct parsing of mboxes
-#
-# Copyright (C) 2016 Intel Corporation
-#
-# SPDX-License-Identifier: GPL-2.0
-
-import base
-import re
-
-class MboxFormat(base.Base):
-
-    def test_mbox_format(self):
-        if self.unidiff_parse_error:
-            self.fail('Series cannot be parsed correctly due to malformed diff lines',
-                      'Create the series again using git-format-patch and ensure it can be applied using git am',
-                      data=[('Diff line', re.sub('^.+:\s(?<!$)','',self.unidiff_parse_error))])
diff --git a/poky/meta/lib/patchtest/tests/test_mbox_mailinglist.py b/poky/meta/lib/patchtest/tests/test_mbox_mailinglist.py
deleted file mode 100644
index de38e20..0000000
--- a/poky/meta/lib/patchtest/tests/test_mbox_mailinglist.py
+++ /dev/null
@@ -1,64 +0,0 @@
-# Check if the series was intended for other project (not OE-Core)
-#
-# Copyright (C) 2017 Intel Corporation
-#
-# SPDX-License-Identifier: GPL-2.0
-
-import subprocess
-import collections
-import base
-import re
-from data import PatchTestInput
-
-class MailingList(base.Base):
-
-    # base paths of main yocto project sub-projects
-    paths = {
-        'oe-core': ['meta-selftest', 'meta-skeleton', 'meta', 'scripts'],
-        'bitbake': ['bitbake'],
-        'documentation': ['documentation'],
-        'poky': ['meta-poky','meta-yocto-bsp'],
-        'oe': ['meta-gpe', 'meta-gnome', 'meta-efl', 'meta-networking', 'meta-multimedia','meta-initramfs', 'meta-ruby', 'contrib', 'meta-xfce', 'meta-filesystems', 'meta-perl', 'meta-webserver', 'meta-systemd', 'meta-oe', 'meta-python']
-        }
-
-    # scripts folder is a mix of oe-core and poky, most is oe-core code except:
-    poky_scripts = ['scripts/yocto-bsp', 'scripts/yocto-kernel', 'scripts/yocto-layer', 'scripts/lib/bsp']
-
-    Project = collections.namedtuple('Project', ['name', 'listemail', 'gitrepo', 'paths'])
-
-    bitbake = Project(name='Bitbake', listemail='bitbake-devel@lists.openembedded.org', gitrepo='http://git.openembedded.org/bitbake/', paths=paths['bitbake'])
-    doc     = Project(name='Documentantion', listemail='yocto@yoctoproject.org', gitrepo='http://git.yoctoproject.org/cgit/cgit.cgi/yocto-docs/', paths=paths['documentation'])
-    poky    = Project(name='Poky', listemail='poky@yoctoproject.org', gitrepo='http://git.yoctoproject.org/cgit/cgit.cgi/poky/', paths=paths['poky'])
-    oe      = Project(name='oe', listemail='openembedded-devel@lists.openembedded.org', gitrepo='http://git.openembedded.org/meta-openembedded/', paths=paths['oe'])
-
-
-    def test_target_mailing_list(self):
-        """In case of merge failure, check for other targeted projects"""
-        if PatchTestInput.repo.ismerged:
-            self.skip('Series merged, no reason to check other mailing lists')
-
-        # a meta project may be indicted in the message subject, if this is the case, just fail
-        # TODO: there may be other project with no-meta prefix, we also need to detect these
-        project_regex = re.compile("\[(?P<project>meta-.+)\]")
-        for commit in MailingList.commits:
-            match = project_regex.match(commit.subject)
-            if match:
-                self.fail('Series sent to the wrong mailing list',
-                          'Check the project\'s README (%s) and send the patch to the indicated list' % match.group('project'),
-                          commit)
-
-        for patch in self.patchset:
-            folders = patch.path.split('/')
-            base_path = folders[0]
-            for project in [self.bitbake, self.doc, self.oe, self.poky]:
-                if base_path in  project.paths:
-                    self.fail('Series sent to the wrong mailing list or some patches from the series correspond to different mailing lists', 'Send the series again to the correct mailing list (ML)',
-                              data=[('Suggested ML', '%s [%s]' % (project.listemail, project.gitrepo)),
-                                    ('Patch\'s path:', patch.path)])
-
-            # check for poky's scripts code
-            if base_path.startswith('scripts'):
-                for poky_file in self.poky_scripts:
-                    if patch.path.startswith(poky_file):
-                        self.fail('Series sent to the wrong mailing list or some patches from the series correspond to different mailing lists', 'Send the series again to the correct mailing list (ML)',
-                                  data=[('Suggested ML', '%s [%s]' % (self.poky.listemail, self.poky.gitrepo)),('Patch\'s path:', patch.path)])
diff --git a/poky/meta/lib/patchtest/tests/test_mbox_merge.py b/poky/meta/lib/patchtest/tests/test_mbox_merge.py
deleted file mode 100644
index c8b6718..0000000
--- a/poky/meta/lib/patchtest/tests/test_mbox_merge.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Check if mbox was merged by patchtest
-#
-# Copyright (C) 2016 Intel Corporation
-#
-# SPDX-License-Identifier: GPL-2.0
-
-import subprocess
-import base
-from data import PatchTestInput
-
-def headlog():
-    output = subprocess.check_output(
-        "cd %s; git log --pretty='%%h#%%aN#%%cD:#%%s' -1" % PatchTestInput.repodir,
-        universal_newlines=True,
-        shell=True
-        )
-    return output.split('#')
-
-class Merge(base.Base):
-    def test_series_merge_on_head(self):
-        if not PatchTestInput.repo.ismerged:
-            commithash, author, date, shortlog = headlog()
-            self.fail('Series does not apply on top of target branch',
-                      'Rebase your series on top of targeted branch',
-                      data=[('Targeted branch', '%s (currently at %s)' % (PatchTestInput.repo.branch, commithash))])
diff --git a/poky/meta/lib/patchtest/tests/test_mbox_shortlog.py b/poky/meta/lib/patchtest/tests/test_mbox_shortlog.py
deleted file mode 100644
index b6c2a20..0000000
--- a/poky/meta/lib/patchtest/tests/test_mbox_shortlog.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Checks related to the patch's  summary
-#
-# Copyright (C) 2016 Intel Corporation
-#
-# SPDX-License-Identifier: GPL-2.0
-
-import base
-import parse_shortlog
-import pyparsing
-
-maxlength = 90
-
-class Shortlog(base.Base):
-
-    def test_shortlog_format(self):
-        for commit in Shortlog.commits:
-            shortlog = commit.shortlog
-            if not shortlog.strip():
-                self.skip('Empty shortlog, no reason to execute shortlog format test')
-            else:
-                # no reason to re-check on revert shortlogs
-                if shortlog.startswith('Revert "'):
-                    continue
-                try:
-                    parse_shortlog.shortlog.parseString(shortlog)
-                except pyparsing.ParseException as pe:
-                    self.fail('Shortlog does not follow expected format',
-                              'Commit shortlog (first line of commit message) should follow the format "<target>: <summary>"',
-                              commit)
-
-    def test_shortlog_length(self):
-        for commit in Shortlog.commits:
-            # no reason to re-check on revert shortlogs
-            shortlog = commit.shortlog
-            if shortlog.startswith('Revert "'):
-                continue
-            l = len(shortlog)
-            if l > maxlength:
-                self.fail('Commit shortlog is too long',
-                          'Edit shortlog so that it is %d characters or less (currently %d characters)' % (maxlength, l),
-                          commit)
diff --git a/poky/meta/lib/patchtest/tests/test_mbox_signed_off_by.py b/poky/meta/lib/patchtest/tests/test_mbox_signed_off_by.py
deleted file mode 100644
index 6458951..0000000
--- a/poky/meta/lib/patchtest/tests/test_mbox_signed_off_by.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# Checks related to the patch's signed-off-by lines
-#
-# Copyright (C) 2016 Intel Corporation
-#
-# SPDX-License-Identifier: GPL-2.0
-
-import base
-import parse_signed_off_by
-import re
-
-class SignedOffBy(base.Base):
-
-    revert_shortlog_regex = re.compile('Revert\s+".*"')
-
-    @classmethod
-    def setUpClassLocal(cls):
-        # match self.mark with no '+' preceding it
-        cls.prog = parse_signed_off_by.signed_off_by
-
-    def test_signed_off_by_presence(self):
-        for commit in SignedOffBy.commits:
-            # skip those patches that revert older commits, these do not required the tag presence
-            if self.revert_shortlog_regex.match(commit.shortlog):
-                continue
-            if not SignedOffBy.prog.search_string(commit.payload):
-                self.fail('Patch is missing Signed-off-by',
-                          'Sign off the patch (either manually or with "git commit --amend -s")',
-                          commit)
diff --git a/poky/meta/lib/patchtest/tests/test_metadata.py b/poky/meta/lib/patchtest/tests/test_metadata.py
new file mode 100644
index 0000000..b6f4456
--- /dev/null
+++ b/poky/meta/lib/patchtest/tests/test_metadata.py
@@ -0,0 +1,180 @@
+# Checks related to the patch's LIC_FILES_CHKSUM  metadata variable
+#
+# Copyright (C) 2016 Intel Corporation
+#
+# SPDX-License-Identifier: GPL-2.0-only
+
+import base
+import os
+import pyparsing
+from data import PatchTestInput, PatchTestDataStore
+
+class TestMetadata(base.Metadata):
+    metadata_lic = 'LICENSE'
+    invalid_license = 'PATCHTESTINVALID'
+    metadata_chksum = 'LIC_FILES_CHKSUM'
+    license_var  = 'LICENSE'
+    closed   = 'CLOSED'
+    lictag_re  = pyparsing.AtLineStart("License-Update:")
+    lic_chksum_added = pyparsing.AtLineStart("+" + metadata_chksum)
+    lic_chksum_removed = pyparsing.AtLineStart("-" + metadata_chksum)
+    add_mark = pyparsing.Regex('\+ ')
+    max_length = 200
+    metadata_src_uri  = 'SRC_URI'
+    md5sum    = 'md5sum'
+    sha256sum = 'sha256sum'
+    git_regex = pyparsing.Regex('^git\:\/\/.*')
+    metadata_summary = 'SUMMARY'
+
+    def test_license_presence(self):
+        if not self.added:
+            self.skip('No added recipes, skipping test')
+
+        # TODO: this is a workaround so we can parse the recipe not
+        # containing the LICENSE var: add some default license instead
+        # of INVALID into auto.conf, then remove this line at the end
+        auto_conf = os.path.join(os.environ.get('BUILDDIR'), 'conf', 'auto.conf')
+        open_flag = 'w'
+        if os.path.exists(auto_conf):
+            open_flag = 'a'
+        with open(auto_conf, open_flag) as fd:
+            for pn in self.added:
+                fd.write('LICENSE ??= "%s"\n' % self.invalid_license)
+
+        no_license = False
+        for pn in self.added:
+            rd = self.tinfoil.parse_recipe(pn)
+            license = rd.getVar(self.metadata_lic)
+            if license == self.invalid_license:
+                no_license = True
+                break
+
+        # remove auto.conf line or the file itself
+        if open_flag == 'w':
+            os.remove(auto_conf)
+        else:
+            fd = open(auto_conf, 'r')
+            lines = fd.readlines()
+            fd.close()
+            with open(auto_conf, 'w') as fd:
+                fd.write(''.join(lines[:-1]))
+
+        if no_license:
+            self.fail('Recipe does not have the LICENSE field set.')
+
+    def test_lic_files_chksum_presence(self):
+        if not self.added:
+            self.skip('No added recipes, skipping test')
+
+        for pn in self.added:
+            rd = self.tinfoil.parse_recipe(pn)
+            pathname = rd.getVar('FILE')
+            # we are not interested in images
+            if '/images/' in pathname:
+                continue
+            lic_files_chksum = rd.getVar(self.metadata_chksum)
+            if rd.getVar(self.license_var) == self.closed:
+                continue
+            if not lic_files_chksum:
+                self.fail('%s is missing in newly added recipe' % self.metadata_chksum)
+
+    def test_lic_files_chksum_modified_not_mentioned(self):
+        if not self.modified:
+            self.skip('No modified recipes, skipping test')
+
+        for patch in self.patchset:
+            # for the moment, we are just interested in metadata
+            if patch.path.endswith('.patch'):
+                continue
+            payload = str(patch)
+            if (self.lic_chksum_added.search_string(payload) or self.lic_chksum_removed.search_string(payload)):
+                # if any patch on the series contain reference on the metadata, fail
+                for commit in self.commits:
+                    if self.lictag_re.search_string(commit.commit_message):
+                       break
+                else:
+                    self.fail('LIC_FILES_CHKSUM changed without "License-Update:" tag and description in commit message')
+
+    def test_max_line_length(self):
+        for patch in self.patchset:
+            # for the moment, we are just interested in metadata
+            if patch.path.endswith('.patch'):
+                continue
+            payload = str(patch)
+            for line in payload.splitlines():
+                if self.add_mark.search_string(line):
+                    current_line_length = len(line[1:])
+                    if current_line_length > self.max_length:
+                        self.fail('Patch line too long (current length %s, maximum is %s)' % (current_line_length, self.max_length),
+                                  data=[('Patch', patch.path), ('Line', '%s ...' % line[0:80])])
+
+    def pretest_src_uri_left_files(self):
+        # these tests just make sense on patches that can be merged
+        if not PatchTestInput.repo.canbemerged:
+            self.skip('Patch cannot be merged')
+        if not self.modified:
+            self.skip('No modified recipes, skipping pretest')
+
+        # get the proper metadata values
+        for pn in self.modified:
+            # we are not interested in images
+            if 'core-image' in pn:
+                continue
+            rd = self.tinfoil.parse_recipe(pn)
+            PatchTestDataStore['%s-%s-%s' % (self.shortid(), self.metadata_src_uri, pn)] = rd.getVar(self.metadata_src_uri)
+
+    def test_src_uri_left_files(self):
+        # these tests just make sense on patches that can be merged
+        if not PatchTestInput.repo.canbemerged:
+            self.skip('Patch cannot be merged')
+        if not self.modified:
+            self.skip('No modified recipes, skipping pretest')
+
+        # get the proper metadata values
+        for pn in self.modified:
+            # we are not interested in images
+            if 'core-image' in pn:
+                continue
+            rd = self.tinfoil.parse_recipe(pn)
+            PatchTestDataStore['%s-%s-%s' % (self.shortid(), self.metadata_src_uri, pn)] = rd.getVar(self.metadata_src_uri)
+
+        for pn in self.modified:
+            pretest_src_uri = PatchTestDataStore['pre%s-%s-%s' % (self.shortid(), self.metadata_src_uri, pn)].split()
+            test_src_uri    = PatchTestDataStore['%s-%s-%s' % (self.shortid(), self.metadata_src_uri, pn)].split()
+
+            pretest_files = set([os.path.basename(patch) for patch in pretest_src_uri if patch.startswith('file://')])
+            test_files    = set([os.path.basename(patch) for patch in test_src_uri    if patch.startswith('file://')])
+
+            # check if files were removed
+            if len(test_files) < len(pretest_files):
+
+                # get removals from patchset
+                filesremoved_from_patchset = set()
+                for patch in self.patchset:
+                    if patch.is_removed_file:
+                        filesremoved_from_patchset.add(os.path.basename(patch.path))
+
+                # get the deleted files from the SRC_URI
+                filesremoved_from_usr_uri = pretest_files - test_files
+
+                # finally, get those patches removed at SRC_URI and not removed from the patchset
+                # TODO: we are not taking into account  renames, so test may raise false positives
+                not_removed = filesremoved_from_usr_uri - filesremoved_from_patchset
+                if not_removed:
+                    self.fail('Patches not removed from tree. Remove them and amend the submitted mbox',
+                              data=[('Patch', f) for f in not_removed])
+
+    def test_summary_presence(self):
+        if not self.added:
+            self.skip('No added recipes, skipping test')
+
+        for pn in self.added:
+            # we are not interested in images
+            if 'core-image' in pn:
+                continue
+            rd = self.tinfoil.parse_recipe(pn)
+            summary = rd.getVar(self.metadata_summary)
+
+            # "${PN} version ${PN}-${PR}" is the default, so fail if default
+            if summary.startswith('%s version' % pn):
+                self.fail('%s is missing in newly added recipe' % self.metadata_summary)
diff --git a/poky/meta/lib/patchtest/tests/test_metadata_lic_files_chksum.py b/poky/meta/lib/patchtest/tests/test_metadata_lic_files_chksum.py
deleted file mode 100644
index e9a5b6b..0000000
--- a/poky/meta/lib/patchtest/tests/test_metadata_lic_files_chksum.py
+++ /dev/null
@@ -1,82 +0,0 @@
-# Checks related to the patch's LIC_FILES_CHKSUM  metadata variable
-#
-# Copyright (C) 2016 Intel Corporation
-#
-# SPDX-License-Identifier: GPL-2.0
-
-import base
-import re
-from data import PatchTestInput, PatchTestDataStore
-
-class LicFilesChkSum(base.Metadata):
-    metadata = 'LIC_FILES_CHKSUM'
-    license  = 'LICENSE'
-    closed   = 'CLOSED'
-    lictag   = 'License-Update'
-    lictag_re  = re.compile("^%s:" % lictag, re.MULTILINE)
-
-    def setUp(self):
-        # these tests just make sense on patches that can be merged
-        if not PatchTestInput.repo.canbemerged:
-            self.skip('Patch cannot be merged')
-
-    def test_lic_files_chksum_presence(self):
-        if not self.added:
-            self.skip('No added recipes, skipping test')
-
-        for pn in self.added:
-            rd = self.tinfoil.parse_recipe(pn)
-            pathname = rd.getVar('FILE')
-            # we are not interested in images
-            if '/images/' in pathname:
-                continue
-            lic_files_chksum = rd.getVar(self.metadata)
-            if rd.getVar(self.license) == self.closed:
-                continue
-            if not lic_files_chksum:
-                self.fail('%s is missing in newly added recipe' % self.metadata,
-                          'Specify the variable %s in %s' % (self.metadata, pn))
-
-    def pretest_lic_files_chksum_modified_not_mentioned(self):
-        if not self.modified:
-            self.skip('No modified recipes, skipping pretest')
-        # get the proper metadata values
-        for pn in self.modified:
-            rd = self.tinfoil.parse_recipe(pn)
-            pathname = rd.getVar('FILE')
-            # we are not interested in images
-            if '/images/' in pathname:
-                continue
-            PatchTestDataStore['%s-%s-%s' % (self.shortid(),self.metadata,pn)] = rd.getVar(self.metadata)
-
-    def test_lic_files_chksum_modified_not_mentioned(self):
-        if not self.modified:
-            self.skip('No modified recipes, skipping test')
-
-        # get the proper metadata values
-        for pn in self.modified:
-            rd = self.tinfoil.parse_recipe(pn)
-            pathname = rd.getVar('FILE')
-            # we are not interested in images
-            if '/images/' in pathname:
-                continue
-            PatchTestDataStore['%s-%s-%s' % (self.shortid(),self.metadata,pn)] = rd.getVar(self.metadata)
-        # compare if there were changes between pre-merge and merge
-        for pn in self.modified:
-            pretest = PatchTestDataStore['pre%s-%s-%s' % (self.shortid(),self.metadata, pn)]
-            test    = PatchTestDataStore['%s-%s-%s' % (self.shortid(),self.metadata, pn)]
-
-            # TODO: this is workaround to avoid false-positives when pretest metadata is empty (not reason found yet)
-            # For more info, check bug 12284
-            if not pretest:
-                return
-
-            if pretest != test:
-                # if any patch on the series contain reference on the metadata, fail
-                for commit in self.commits:
-                    if self.lictag_re.search(commit.commit_message):
-                       break
-                else:
-                    self.fail('LIC_FILES_CHKSUM changed on target %s but there is no "%s" tag in commit message' % (pn, self.lictag),
-                              'Include "%s: <description>" into the commit message with a brief description' % self.lictag,
-                              data=[('Current checksum', pretest), ('New checksum', test)])
diff --git a/poky/meta/lib/patchtest/tests/test_metadata_license.py b/poky/meta/lib/patchtest/tests/test_metadata_license.py
deleted file mode 100644
index 16604db..0000000
--- a/poky/meta/lib/patchtest/tests/test_metadata_license.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# Checks related to the patch's LIC_FILES_CHKSUM  metadata variable
-#
-# Copyright (C) 2016 Intel Corporation
-#
-# SPDX-License-Identifier: GPL-2.0
-
-import base
-import os
-from data import PatchTestInput
-
-class License(base.Metadata):
-    metadata = 'LICENSE'
-    invalid_license = 'PATCHTESTINVALID'
-
-    def setUp(self):
-        # these tests just make sense on patches that can be merged
-        if not PatchTestInput.repo.canbemerged:
-            self.skip('Patch cannot be merged')
-
-    def test_license_presence(self):
-        if not self.added:
-            self.skip('No added recipes, skipping test')
-
-        # TODO: this is a workaround so we can parse the recipe not
-        # containing the LICENSE var: add some default license instead
-        # of INVALID into auto.conf, then remove this line at the end
-        auto_conf = os.path.join(os.environ.get('BUILDDIR'), 'conf', 'auto.conf')
-        open_flag = 'w'
-        if os.path.exists(auto_conf):
-            open_flag = 'a'
-        with open(auto_conf, open_flag) as fd:
-            for pn in self.added:
-                fd.write('LICENSE ??= "%s"\n' % self.invalid_license)
-
-        no_license = False
-        for pn in self.added:
-            rd = self.tinfoil.parse_recipe(pn)
-            license = rd.getVar(self.metadata)
-            if license == self.invalid_license:
-                no_license = True
-                break
-
-        # remove auto.conf line or the file itself
-        if open_flag == 'w':
-            os.remove(auto_conf)
-        else:
-            fd = open(auto_conf, 'r')
-            lines = fd.readlines()
-            fd.close()
-            with open(auto_conf, 'w') as fd:
-                fd.write(''.join(lines[:-1]))
-
-        if no_license:
-            self.fail('Recipe does not have the LICENSE field set', 'Include a LICENSE into the new recipe')
-
diff --git a/poky/meta/lib/patchtest/tests/test_metadata_max_length.py b/poky/meta/lib/patchtest/tests/test_metadata_max_length.py
deleted file mode 100644
index 04a5e23..0000000
--- a/poky/meta/lib/patchtest/tests/test_metadata_max_length.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# Checks related to patch line lengths
-#
-# Copyright (C) 2016 Intel Corporation
-#
-# SPDX-License-Identifier: GPL-2.0
-
-import base
-import re
-
-class MaxLength(base.Base):
-    add_mark = re.compile('\+ ')
-    max_length = 200
-
-    def test_max_line_length(self):
-        for patch in self.patchset:
-            # for the moment, we are just interested in metadata
-            if patch.path.endswith('.patch'):
-                continue
-            payload = str(patch)
-            for line in payload.splitlines():
-                if self.add_mark.match(line):
-                    current_line_length = len(line[1:])
-                    if current_line_length > self.max_length:
-                        self.fail('Patch line too long (current length %s)' % current_line_length,
-                                  'Shorten the corresponding patch line (max length supported %s)' % self.max_length,
-                                  data=[('Patch', patch.path), ('Line', '%s ...' % line[0:80])])
diff --git a/poky/meta/lib/patchtest/tests/test_metadata_src_uri.py b/poky/meta/lib/patchtest/tests/test_metadata_src_uri.py
deleted file mode 100644
index 718229d..0000000
--- a/poky/meta/lib/patchtest/tests/test_metadata_src_uri.py
+++ /dev/null
@@ -1,75 +0,0 @@
-# Checks related to the patch's SRC_URI metadata variable
-#
-# Copyright (C) 2016 Intel Corporation
-#
-# SPDX-License-Identifier: GPL-2.0
-
-import subprocess
-import base
-import re
-import os
-from data import PatchTestInput, PatchTestDataStore
-
-class SrcUri(base.Metadata):
-
-    metadata  = 'SRC_URI'
-    md5sum    = 'md5sum'
-    sha256sum = 'sha256sum'
-    git_regex = re.compile('^git\:\/\/.*')
-
-    def setUp(self):
-        # these tests just make sense on patches that can be merged
-        if not PatchTestInput.repo.canbemerged:
-            self.skip('Patch cannot be merged')
-
-    def pretest_src_uri_left_files(self):
-        if not self.modified:
-            self.skip('No modified recipes, skipping pretest')
-
-        # get the proper metadata values
-        for pn in self.modified:
-            # we are not interested in images
-            if 'core-image' in pn:
-                continue
-            rd = self.tinfoil.parse_recipe(pn)
-            PatchTestDataStore['%s-%s-%s' % (self.shortid(), self.metadata, pn)] = rd.getVar(self.metadata)
-
-    def test_src_uri_left_files(self):
-        if not self.modified:
-            self.skip('No modified recipes, skipping pretest')
-
-        # get the proper metadata values
-        for pn in self.modified:
-            # we are not interested in images
-            if 'core-image' in pn:
-                continue
-            rd = self.tinfoil.parse_recipe(pn)
-            PatchTestDataStore['%s-%s-%s' % (self.shortid(), self.metadata, pn)] = rd.getVar(self.metadata)
-
-        for pn in self.modified:
-            pretest_src_uri = PatchTestDataStore['pre%s-%s-%s' % (self.shortid(), self.metadata, pn)].split()
-            test_src_uri    = PatchTestDataStore['%s-%s-%s' % (self.shortid(), self.metadata, pn)].split()
-
-            pretest_files = set([os.path.basename(patch) for patch in pretest_src_uri if patch.startswith('file://')])
-            test_files    = set([os.path.basename(patch) for patch in test_src_uri    if patch.startswith('file://')])
-
-            # check if files were removed
-            if len(test_files) < len(pretest_files):
-
-                # get removals from patchset
-                filesremoved_from_patchset = set()
-                for patch in self.patchset:
-                    if patch.is_removed_file:
-                        filesremoved_from_patchset.add(os.path.basename(patch.path))
-
-                # get the deleted files from the SRC_URI
-                filesremoved_from_usr_uri = pretest_files - test_files
-
-                # finally, get those patches removed at SRC_URI and not removed from the patchset
-                # TODO: we are not taking into account  renames, so test may raise false positives
-                not_removed = filesremoved_from_usr_uri - filesremoved_from_patchset
-                if not_removed:
-                    self.fail('Patches not removed from tree',
-                              'Amend the patch containing the software patch file removal',
-                              data=[('Patch', f) for f in not_removed])
-
diff --git a/poky/meta/lib/patchtest/tests/test_metadata_summary.py b/poky/meta/lib/patchtest/tests/test_metadata_summary.py
deleted file mode 100644
index 931b267..0000000
--- a/poky/meta/lib/patchtest/tests/test_metadata_summary.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# Checks related to the patch's summary metadata variable
-#
-# Copyright (C) 2016 Intel Corporation
-#
-# SPDX-License-Identifier: GPL-2.0
-
-import base
-from data import PatchTestInput
-
-class Summary(base.Metadata):
-    metadata = 'SUMMARY'
-
-    def setUp(self):
-        # these tests just make sense on patches that can be merged
-        if not PatchTestInput.repo.canbemerged:
-            self.skip('Patch cannot be merged')
-
-    def test_summary_presence(self):
-        if not self.added:
-            self.skip('No added recipes, skipping test')
-
-        for pn in self.added:
-            # we are not interested in images
-            if 'core-image' in pn:
-                continue
-            rd = self.tinfoil.parse_recipe(pn)
-            summary = rd.getVar(self.metadata)
-
-            # "${PN} version ${PN}-${PR}" is the default, so fail if default
-            if summary.startswith('%s version' % pn):
-                self.fail('%s is missing in newly added recipe' % self.metadata,
-                          'Specify the variable %s in %s' % (self.metadata, pn))
diff --git a/poky/meta/lib/patchtest/tests/test_patch.py b/poky/meta/lib/patchtest/tests/test_patch.py
new file mode 100644
index 0000000..65d0f93
--- /dev/null
+++ b/poky/meta/lib/patchtest/tests/test_patch.py
@@ -0,0 +1,102 @@
+# Checks related to the patch's CVE lines
+#
+# Copyright (C) 2016 Intel Corporation
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+
+import base
+import parse_signed_off_by
+import parse_upstream_status
+import pyparsing
+
+class TestPatch(base.Base):
+
+    re_cve_pattern = pyparsing.Regex("CVE\-\d{4}\-\d+")
+    re_cve_payload_tag = pyparsing.Regex("\+CVE:(\s+CVE\-\d{4}\-\d+)+")
+    upstream_status_regex = pyparsing.AtLineStart("+" + "Upstream-Status")
+
+    @classmethod
+    def setUpClassLocal(cls):
+        cls.newpatches = []
+        # get just those relevant patches: new software patches
+        for patch in cls.patchset:
+            if patch.path.endswith('.patch') and patch.is_added_file:
+                cls.newpatches.append(patch)
+
+        cls.mark = str(parse_signed_off_by.signed_off_by_mark).strip('"')
+
+        # match PatchSignedOffBy.mark with '+' preceding it
+        cls.prog = parse_signed_off_by.patch_signed_off_by
+
+    def setUp(self):
+        if self.unidiff_parse_error:
+            self.skip('Parse error %s' % self.unidiff_parse_error)
+
+        self.valid_status    = ', '.join(parse_upstream_status.upstream_status_nonliteral_valid_status)
+        self.standard_format = 'Upstream-Status: <Valid status>'
+
+        # we are just interested in series that introduce CVE patches, thus discard other
+        # possibilities: modification to current CVEs, patch directly introduced into the
+        # recipe, upgrades already including the CVE, etc.
+        new_cves = [p for p in self.patchset if p.path.endswith('.patch') and p.is_added_file]
+        if not new_cves:
+            self.skip('No new CVE patches introduced')
+
+    def test_upstream_status_presence_format(self):
+        if not TestPatch.newpatches:
+            self.skip("There are no new software patches, no reason to test Upstream-Status presence/format")
+
+        for newpatch in TestPatch.newpatches:
+            payload = newpatch.__str__()
+            if not self.upstream_status_regex.search_string(payload):
+                self.fail('Added patch file is missing Upstream-Status: <Valid status> in the commit message',
+                          data=[('Standard format', self.standard_format), ('Valid status', self.valid_status)])
+            for line in payload.splitlines():
+                if self.patchmetadata_regex.match(line):
+                    continue
+                if self.upstream_status_regex.search_string(line):
+                        if parse_upstream_status.inappropriate_status_mark.searchString(line):
+                            try:
+                                parse_upstream_status.upstream_status_inappropriate_info.parseString(line.lstrip('+'))
+                            except pyparsing.ParseException as pe:
+                                self.fail('Upstream-Status is Inappropriate, but no reason was provided',
+                                          data=[('Current', pe.pstr), ('Standard format', 'Upstream-Status: Inappropriate [reason]')])
+                        elif parse_upstream_status.submitted_status_mark.searchString(line):
+                            try:
+                                parse_upstream_status.upstream_status_submitted_info.parseString(line.lstrip('+'))
+                            except pyparsing.ParseException as pe:
+                                self.fail('Upstream-Status is Submitted, but it is not mentioned where',
+                                          data=[('Current', pe.pstr), ('Standard format', 'Upstream-Status: Submitted [where]')])
+                        else:
+                            try:
+                                parse_upstream_status.upstream_status.parseString(line.lstrip('+'))
+                            except pyparsing.ParseException as pe:
+                                self.fail('Upstream-Status is in incorrect format',
+                                          data=[('Current', pe.pstr), ('Standard format', self.standard_format), ('Valid status', self.valid_status)])
+
+    def test_signed_off_by_presence(self):
+        if not TestPatch.newpatches:
+            self.skip("There are no new software patches, no reason to test %s presence" % PatchSignedOffBy.mark)
+
+        for newpatch in TestPatch.newpatches:
+            payload = newpatch.__str__()
+            for line in payload.splitlines():
+                if self.patchmetadata_regex.match(line):
+                    continue
+                if TestPatch.prog.search_string(payload):
+                    break
+            else:
+                self.fail('A patch file has been added without a Signed-off-by tag. Sign off the added patch file (%s)' % newpatch.path)
+
+    def test_cve_tag_format(self):
+        for commit in TestPatch.commits:
+            if self.re_cve_pattern.search_string(commit.shortlog) or self.re_cve_pattern.search_string(commit.commit_message):
+                tag_found = False
+                for line in commit.payload.splitlines():
+                    if self.re_cve_payload_tag.search_string(line):
+                        tag_found = True
+                        break
+                if not tag_found:
+                    self.fail('Missing or incorrectly formatted CVE tag in patch file. Correct or include the CVE tag in the patch with format: "CVE: CVE-YYYY-XXXX"',
+                              commit=commit)
diff --git a/poky/meta/lib/patchtest/tests/test_patch_cve.py b/poky/meta/lib/patchtest/tests/test_patch_cve.py
deleted file mode 100644
index 46ed9ef..0000000
--- a/poky/meta/lib/patchtest/tests/test_patch_cve.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# Checks related to the patch's CVE lines
-#
-# Copyright (C) 2016 Intel Corporation
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-# SPDX-License-Identifier: GPL-2.0-or-later
-
-import base
-import os
-import re
-
-class CVE(base.Base):
-
-    re_cve_pattern = re.compile("CVE\-\d{4}\-\d+", re.IGNORECASE)
-    re_cve_payload_tag     = re.compile("\+CVE:(\s+CVE\-\d{4}\-\d+)+")
-
-    def setUp(self):
-        if self.unidiff_parse_error:
-            self.skip('Parse error %s' % self.unidiff_parse_error)
-
-        # we are just interested in series that introduce CVE patches, thus discard other
-        # possibilities: modification to current CVEs, patch directly introduced into the
-        # recipe, upgrades already including the CVE, etc.
-        new_cves = [p for p in self.patchset if p.path.endswith('.patch') and p.is_added_file]
-        if not new_cves:
-            self.skip('No new CVE patches introduced')
-
-    def test_cve_tag_format(self):
-        for commit in CVE.commits:
-            if self.re_cve_pattern.search(commit.shortlog) or self.re_cve_pattern.search(commit.commit_message):
-                tag_found = False
-                for line in commit.payload.splitlines():
-                    if self.re_cve_payload_tag.match(line):
-                        tag_found = True
-                        break
-                if not tag_found:
-                    self.fail('Missing or incorrectly formatted CVE tag in included patch file',
-                              'Correct or include the CVE tag on cve patch with format: "CVE: CVE-YYYY-XXXX"',
-                              commit)
diff --git a/poky/meta/lib/patchtest/tests/test_patch_signed_off_by.py b/poky/meta/lib/patchtest/tests/test_patch_signed_off_by.py
deleted file mode 100644
index 4855d6d..0000000
--- a/poky/meta/lib/patchtest/tests/test_patch_signed_off_by.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# Checks related to the patch's signed-off-by lines
-#
-# Copyright (C) 2016 Intel Corporation
-#
-# SPDX-License-Identifier: GPL-2.0
-
-import base
-import parse_signed_off_by
-import re
-
-class PatchSignedOffBy(base.Base):
-
-    @classmethod
-    def setUpClassLocal(cls):
-        cls.newpatches = []
-        # get just those relevant patches: new software patches
-        for patch in cls.patchset:
-            if patch.path.endswith('.patch') and patch.is_added_file:
-                cls.newpatches.append(patch)
-
-        cls.mark = str(parse_signed_off_by.signed_off_by_mark).strip('"')
-
-        # match PatchSignedOffBy.mark with '+' preceding it
-        cls.prog = parse_signed_off_by.patch_signed_off_by
-
-    def setUp(self):
-        if self.unidiff_parse_error:
-            self.skip('Parse error %s' % self.unidiff_parse_error)
-
-    def test_signed_off_by_presence(self):
-        if not PatchSignedOffBy.newpatches:
-            self.skip("There are no new software patches, no reason to test %s presence" % PatchSignedOffBy.mark)
-
-        for newpatch in PatchSignedOffBy.newpatches:
-            payload = newpatch.__str__()
-            for line in payload.splitlines():
-                if self.patchmetadata_regex.match(line):
-                    continue
-                if PatchSignedOffBy.prog.search_string(payload):
-                    break
-            else:
-                self.fail('A patch file has been added, but does not have a Signed-off-by tag',
-                          'Sign off the added patch file (%s)' % newpatch.path)
diff --git a/poky/meta/lib/patchtest/tests/test_patch_upstream_status.py b/poky/meta/lib/patchtest/tests/test_patch_upstream_status.py
deleted file mode 100644
index eda5353..0000000
--- a/poky/meta/lib/patchtest/tests/test_patch_upstream_status.py
+++ /dev/null
@@ -1,64 +0,0 @@
-# Checks related to the patch's upstream-status lines
-#
-# Copyright (C) 2016 Intel Corporation
-#
-# SPDX-License-Identifier: GPL-2.0
-
-import base
-import parse_upstream_status
-import pyparsing
-import os
-
-class PatchUpstreamStatus(base.Base):
-
-    upstream_status_regex = pyparsing.AtLineStart("+" + "Upstream-Status")
-
-    @classmethod
-    def setUpClassLocal(cls):
-        cls.newpatches = []
-        # get just those relevant patches: new software patches
-        for patch in cls.patchset:
-            if patch.path.endswith('.patch') and patch.is_added_file:
-                cls.newpatches.append(patch)
-
-    def setUp(self):
-        if self.unidiff_parse_error:
-            self.skip('Python-unidiff parse error')
-        self.valid_status    = ', '.join(parse_upstream_status.upstream_status_nonliteral_valid_status)
-        self.standard_format = 'Upstream-Status: <Valid status>'
-
-    def test_upstream_status_presence_format(self):
-        if not PatchUpstreamStatus.newpatches:
-            self.skip("There are no new software patches, no reason to test Upstream-Status presence/format")
-
-        for newpatch in PatchUpstreamStatus.newpatches:
-            payload = newpatch.__str__()
-            if not self.upstream_status_regex.search_string(payload):
-                self.fail('Added patch file is missing Upstream-Status in the header',
-                          'Add Upstream-Status: <Valid status> to the header of %s' % newpatch.path,
-                          data=[('Standard format', self.standard_format), ('Valid status', self.valid_status)])
-            for line in payload.splitlines():
-                if self.patchmetadata_regex.match(line):
-                    continue
-                if self.upstream_status_regex.search_string(line):
-                        if parse_upstream_status.inappropriate_status_mark.searchString(line):
-                            try:
-                                parse_upstream_status.upstream_status_inappropriate_info.parseString(line.lstrip('+'))
-                            except pyparsing.ParseException as pe:
-                                self.fail('Upstream-Status is Inappropriate, but no reason was provided',
-                                          'Include a brief reason why %s is inappropriate' % os.path.basename(newpatch.path),
-                                          data=[('Current', pe.pstr), ('Standard format', 'Upstream-Status: Inappropriate [reason]')])
-                        elif parse_upstream_status.submitted_status_mark.searchString(line):
-                            try:
-                                parse_upstream_status.upstream_status_submitted_info.parseString(line.lstrip('+'))
-                            except pyparsing.ParseException as pe:
-                                self.fail('Upstream-Status is Submitted, but it is not mentioned where',
-                                          'Include where %s was submitted' % os.path.basename(newpatch.path),
-                                          data=[('Current', pe.pstr), ('Standard format', 'Upstream-Status: Submitted [where]')])
-                        else:
-                            try:
-                                parse_upstream_status.upstream_status.parseString(line.lstrip('+'))
-                            except pyparsing.ParseException as pe:
-                                self.fail('Upstream-Status is in incorrect format',
-                                          'Fix Upstream-Status format in %s' % os.path.basename(newpatch.path),
-                                          data=[('Current', pe.pstr), ('Standard format', self.standard_format), ('Valid status', self.valid_status)])
diff --git a/poky/meta/lib/patchtest/tests/test_python_pylint.py b/poky/meta/lib/patchtest/tests/test_python_pylint.py
index ea8efb7..ef315e5 100644
--- a/poky/meta/lib/patchtest/tests/test_python_pylint.py
+++ b/poky/meta/lib/patchtest/tests/test_python_pylint.py
@@ -2,11 +2,14 @@
 #
 # Copyright (C) 2016 Intel Corporation
 #
-# SPDX-License-Identifier: GPL-2.0
+# SPDX-License-Identifier: GPL-2.0-only
 
 import base
+from io import StringIO
 from data import PatchTestInput
-import pylint.epylint as lint
+from pylint.reporters.text import TextReporter
+import pylint.lint as lint
+
 
 class PyLint(base.Base):
     pythonpatches  = []
@@ -26,16 +29,16 @@
     def setUp(self):
         if self.unidiff_parse_error:
             self.skip('Python-unidiff parse error')
-        if not PatchTestInput.repo.canbemerged:
-            self.skip('Patch cannot be merged, no reason to execute the test method')
         if not PyLint.pythonpatches:
             self.skip('No python related patches, skipping test')
 
     def pretest_pylint(self):
         for pythonpatch in self.pythonpatches:
             if pythonpatch.is_modified_file:
-                (pylint_stdout, pylint_stderr) = lint.py_run(command_options = pythonpatch.path + self.pylint_options, return_std=True)
-                for line in pylint_stdout.readlines():
+                pylint_output = StringIO()
+                reporter = TextReporter(pylint_output)
+                lint.Run([self.pylint_options, pythonpatch.path], reporter=reporter, exit=False)
+                for line in pylint_output.readlines():
                     if not '*' in line:
                         if line.strip():
                             self.pylint_pretest[line.strip().split(' ',1)[0]] = line.strip().split(' ',1)[1]
@@ -48,14 +51,15 @@
                 path = pythonpatch.target_file[2:]
             else:
                 path = pythonpatch.path
-            (pylint_stdout, pylint_stderr) = lint.py_run(command_options = path + self.pylint_options, return_std=True)
-            for line in pylint_stdout.readlines():
+            pylint_output = StringIO()
+            reporter = TextReporter(pylint_output)
+            lint.Run([self.pylint_options, pythonpatch.path], reporter=reporter, exit=False)
+            for line in pylint_output.readlines():
                     if not '*' in line:
                         if line.strip():
                             self.pylint_test[line.strip().split(' ',1)[0]] = line.strip().split(' ',1)[1]
 
         for issue in self.pylint_test:
              if self.pylint_test[issue] not in self.pylint_pretest.values():
-                 self.fail('Errors in your Python code were encountered',
-                           'Correct the lines introduced by your patch',
+                 self.fail('Errors in your Python code were encountered. Please check your code with a linter and resubmit',
                            data=[('Output', 'Please, fix the listed issues:'), ('', issue + ' ' + self.pylint_test[issue])])
diff --git a/poky/meta/lib/patchtest/utils.py b/poky/meta/lib/patchtest/utils.py
new file mode 100644
index 0000000..a4a523b
--- /dev/null
+++ b/poky/meta/lib/patchtest/utils.py
@@ -0,0 +1,168 @@
+# ex:ts=4:sw=4:sts=4:et
+# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
+#
+# utils: common methods used by the patchtest framework
+#
+# Copyright (C) 2016 Intel Corporation
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+
+import os
+import subprocess
+import logging
+import re
+import mailbox
+
+class CmdException(Exception):
+    """ Simple exception class where its attributes are the ones passed when instantiated """
+    def __init__(self, cmd):
+        self._cmd = cmd
+    def __getattr__(self, name):
+        value = None
+        if self._cmd.has_key(name):
+            value = self._cmd[name]
+        return value
+
+def exec_cmd(cmd, cwd, ignore_error=False, input=None, strip=True, updateenv={}):
+    """
+         Input:
+
+            cmd: dict containing the following keys:
+
+                cmd : the command itself as an array of strings
+                ignore_error: if False, no exception is raised
+                strip: indicates if strip is done on the output (stdout and stderr)
+                input: input data to the command (stdin)
+                updateenv: environment variables to be appended to the current
+                process environment variables
+
+            NOTE: keys 'ignore_error' and 'input' are optional; if not included,
+            the defaults are the ones specify in the arguments
+            cwd: directory where commands are executed
+            ignore_error: raise CmdException if command fails to execute and
+            this value is False
+            input: input data (stdin) for the command
+
+         Output: dict containing the following keys:
+
+             cmd: the same as input
+             ignore_error: the same as input
+             strip: the same as input
+             input: the same as input
+             stdout: Standard output after command's execution
+             stderr: Standard error after command's execution
+             returncode: Return code after command's execution
+
+    """
+    cmddefaults = {
+        'cmd':'',
+        'ignore_error':ignore_error,
+        'strip':strip,
+        'input':input,
+        'updateenv':updateenv,
+    }
+
+    # update input values if necessary
+    cmddefaults.update(cmd)
+
+    _cmd = cmddefaults
+
+    if not _cmd['cmd']:
+        raise CmdException({'cmd':None, 'stderr':'no command given'})
+
+    # update the environment
+    env = os.environ
+    env.update(_cmd['updateenv'])
+
+    _command = [e for e in _cmd['cmd']]
+    p = subprocess.Popen(_command,
+                         stdin=subprocess.PIPE,
+                         stdout=subprocess.PIPE,
+                         stderr=subprocess.PIPE,
+                         universal_newlines=True,
+                         cwd=cwd,
+                         env=env)
+
+    # execute the command and strip output
+    (_stdout, _stderr) = p.communicate(_cmd['input'])
+    if _cmd['strip']:
+        _stdout, _stderr = map(str.strip, [_stdout, _stderr])
+
+    # generate the result
+    result = _cmd
+    result.update({'cmd':_command,'stdout':_stdout,'stderr':_stderr,'returncode':p.returncode})
+
+    # launch exception if necessary
+    if not _cmd['ignore_error'] and p.returncode:
+        raise CmdException(result)
+
+    return result
+
+def exec_cmds(cmds, cwd):
+    """ Executes commands
+
+         Input:
+             cmds: Array of commands
+             cwd: directory where commands are executed
+
+         Output: Array of output commands
+    """
+    results = []
+    _cmds = cmds
+
+    for cmd in _cmds:
+        result = exec_cmd(cmd, cwd)
+        results.append(result)
+
+    return results
+
+def logger_create(name):
+    logger = logging.getLogger(name)
+    loggerhandler = logging.StreamHandler()
+    loggerhandler.setFormatter(logging.Formatter("%(message)s"))
+    logger.addHandler(loggerhandler)
+    logger.setLevel(logging.INFO)
+    return logger
+
+def get_subject_prefix(path):
+    prefix = ""
+    mbox = mailbox.mbox(path)
+
+    if len(mbox):
+        subject = mbox[0]['subject']
+        if subject:
+            pattern = re.compile("(\[.*\])", re.DOTALL)
+            match = pattern.search(subject)
+            if match:
+                prefix = match.group(1)
+
+    return prefix
+
+def valid_branch(branch):
+    """ Check if branch is valid name """
+    lbranch = branch.lower()
+
+    invalid  = lbranch.startswith('patch') or \
+               lbranch.startswith('rfc') or \
+               lbranch.startswith('resend') or \
+               re.search('^v\d+', lbranch) or \
+               re.search('^\d+/\d+', lbranch)
+
+    return not invalid
+
+def get_branch(path):
+    """ Get the branch name from mbox """
+    fullprefix = get_subject_prefix(path)
+    branch, branches, valid_branches = None, [], []
+
+    if fullprefix:
+        prefix = fullprefix.strip('[]')
+        branches = [ b.strip() for b in prefix.split(',')]
+        valid_branches = [b for b in branches if valid_branch(b)]
+
+    if len(valid_branches):
+        branch = valid_branches[0]
+
+    return branch
+