diff --git a/.github/workflows/third_party_notices.yml b/.github/workflows/third_party_notices.yml new file mode 100644 index 0000000000..24f63715d7 --- /dev/null +++ b/.github/workflows/third_party_notices.yml @@ -0,0 +1,19 @@ +--- +# yamllint disable rule:line-length +name: Check for updated Third Party Notices + +on: # yamllint disable-line rule:truthy + pull_request: + +jobs: + third-party-notices: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5.1.0 + with: + python-version: '3.12' + - name: Run the generator + run: | + python Support/Python/unitybuild/generate_notice.py + git diff --exit-code diff --git a/Support/Python/unitybuild/generate_notice.py b/Support/Python/unitybuild/generate_notice.py new file mode 100644 index 0000000000..28a5e19863 --- /dev/null +++ b/Support/Python/unitybuild/generate_notice.py @@ -0,0 +1,81 @@ +# Copyright 2020 The Tilt Brush Authors +# Copyright 2024 The Open Brush Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import io +import re +import codecs + + +class BuildFailed(Exception): + pass + + +def create_notice_file(project_dir): + def iter_notice_files(): + """Yields (library_name, notice_file_name) tuples.""" + root = os.path.join(project_dir, "Assets/ThirdParty") + if not os.path.exists(root): + raise BuildFailed("Cannot generate NOTICE: missing %s" % root) + for r, _, fs in os.walk(root): + for f in fs: + if f.lower() in ( + "notice", + "notice.txt", + "notice.tiltbrush", + "notice.md", + ): + yield (os.path.basename(r), os.path.join(r, f)) + root = os.path.join(project_dir, "Assets/ThirdParty/NuGet/Packages") + if not os.path.exists(root): + raise BuildFailed("Cannot generate NOTICE: missing %s" % root) + for r, _, fs in os.walk(root): + for f in fs: + if f.lower() in ("notice", "notice.md", "notice.txt"): + m = re.match(r"\D+", os.path.basename(r)) + if m: + name = m.group(0).rstrip(".") + if name[-2:] == ".v" or name[-2:] == ".V": + name = name[:-2] + yield (name, os.path.join(r, f)) + + tmpf = io.StringIO() + tmpf.write( + """This file is automatically generated. +This software makes use of third-party software with the following notices. +""" + ) + for library_name, notice_file in iter_notice_files(): + tmpf.write("\n \n=== %s ===\n" % library_name) + with open(notice_file) as inf: + contents = inf.read() + if contents.startswith(str(codecs.BOM_UTF8)): + contents = contents[len(codecs.BOM_UTF8) :] + tmpf.write(contents) + tmpf.write("\n") + + output_filename = os.path.join( + project_dir, "Support/ThirdParty/GeneratedThirdPartyNotices.txt" + ) + with open(output_filename, "w") as outf: + outf.write(tmpf.getvalue()) + + +def main(project_dir): + create_notice_file(project_dir) + + +if __name__ == "__main__": + create_notice_file(os.getcwd())