proj-flow 0.11.3__tar.gz → 0.13.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {proj_flow-0.11.3 → proj_flow-0.13.0}/CHANGELOG.rst +16 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/PKG-INFO +1 -1
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/__init__.py +1 -1
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/api/release.py +18 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/base/cmd.py +12 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/ext/cplusplus/cmake/__init__.py +2 -2
- proj_flow-0.11.3/src/proj_flow/ext/cplusplus/cmake/version.py → proj_flow-0.13.0/src/proj_flow/ext/cplusplus/cmake/project.py +0 -4
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/ext/github/cli.py +66 -6
- proj_flow-0.13.0/src/proj_flow/ext/github/publishing.py +65 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/ext/python/rtdocs.py +2 -11
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/ext/store.py +1 -11
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/flow/configs.py +42 -1
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/log/commit.py +45 -2
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/log/hosting/github.py +72 -6
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/log/release.py +4 -3
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/base/.flow/config.yml +0 -8
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/base/.flow/flow.py.mustache +19 -12
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/github_actions/.github/workflows/build.yml +3 -5
- {proj_flow-0.11.3 → proj_flow-0.13.0}/.flow/config.yaml +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/.gitignore +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/.readthedocs.yaml +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/LICENSE +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/README.md +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/pyproject.toml +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/__main__.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/api/__init__.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/api/arg.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/api/completers.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/api/ctx.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/api/env.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/api/init.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/api/makefile.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/api/step.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/base/__cmake_version__.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/base/__init__.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/base/inspect.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/base/matrix.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/base/name_list.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/base/plugins.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/base/registry.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/base/uname.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/cli/__init__.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/cli/argument.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/cli/finder.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/dependency.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/ext/__init__.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/ext/cplusplus/__init__.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/ext/cplusplus/cmake/parser.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/ext/cplusplus/cmake/steps.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/ext/cplusplus/conan/__init__.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/ext/cplusplus/conan/_conan.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/ext/github/__init__.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/ext/github/hosting.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/ext/github/switches.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/ext/markdown_changelog.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/ext/python/__init__.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/ext/python/steps.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/ext/python/version.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/ext/re_structured_changelog.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/ext/sign/__init__.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/ext/sign/api.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/ext/sign/win32.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/flow/__init__.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/flow/layer.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/flow/steps.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/log/__init__.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/log/error.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/log/fmt.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/log/format.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/log/hosting/__init__.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/log/msg.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/log/rich_text/__init__.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/log/rich_text/api.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/log/rich_text/markdown.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/log/rich_text/re_structured_text.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/minimal/__init__.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/minimal/base.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/minimal/bootstrap.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/minimal/init.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/minimal/list.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/minimal/run.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/minimal/system.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/project/__init__.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/project/api.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/project/cplusplus/__init__.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/project/cplusplus/cmake_context.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/project/cplusplus/conan_context.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/project/cplusplus/project.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/project/data.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/project/interact.py +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/base/.clang-format +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/base/.flow/matrix.yml +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/base/.flow/official.yml +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/base/.gitignore +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/base/README.md.mustache +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/base/flow +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/base/flow.cmd +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/base.json +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/cmake/.flow/cmake/common.cmake.mustache +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/cmake/.flow/extensions/wall/__init__.py.mustache +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/cmake/.flow/extensions/wall/icons/__init__.py.mustache +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/cmake/.flow/extensions/wall/icons/magick.py.mustache +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/cmake/.flow/packages/base.cmake.mustache +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/cmake/.flow/packages/config.cmake +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/cmake/.flow/packages/cpack.cmake +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/cmake/.flow/packages/wix/cpack.cmake.mustache +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/cmake/.flow/packages/wix/patches.in.wix +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/cmake/.flow/packages/wix.cmake.mustache +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/cmake/CMakeGraphVizOptions.cmake +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/cmake/CMakeLists.txt.mustache +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/cmake/CMakePresets.json +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/cmake/data/assets/appicon.ico +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/cmake/data/assets/appicon.png +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/cmake/data/assets/wix_banner.bmp +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/cmake/data/assets/wix_dialog.bmp +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/cmake/data/icons/.gitignore +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/cmake/data/icons/appicon-mask.svg +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/cmake/data/icons/appicon.svg +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/cmake/src/main.cc.mustache +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/cmake/src/version.hpp.in.mustache +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/cmake/tests/test.cc.mustache +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/cmake.json +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/conan/.flow/cmake/libcxx_toolchain.cmake +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/conan/.flow/cmake/output_dirs_setup.cmake +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/conan/conanfile.txt +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/conan.json +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/github_actions/.github/linters/.isort.cfg +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/github_actions/.github/linters/.mypy.ini +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/github_actions/.github/workflows/linter.yml +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/github_actions/CPPLINT.cfg +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/github_actions.json +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/github_social/.github/ISSUE_TEMPLATE/bug_report.md.mustache +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/github_social/.github/ISSUE_TEMPLATE/feature_request.md.mustache +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/github_social/CODE_OF_CONDUCT.md.mustache +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/github_social/CONTRIBUTING.md +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/layers/github_social.json +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/licenses/0BSD.mustache +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/licenses/MIT.mustache +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/licenses/Unlicense.mustache +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/licenses/WTFPL.mustache +0 -0
- {proj_flow-0.11.3 → proj_flow-0.13.0}/src/proj_flow/template/licenses/Zlib.mustache +0 -0
|
@@ -4,6 +4,22 @@ Changelog
|
|
|
4
4
|
|
|
5
5
|
All notable changes to this project will be documented in this file.
|
|
6
6
|
|
|
7
|
+
`0.13.0 <https://github.com/mzdun/proj-flow/compare/v0.12.0...v0.13.0>`_ (2025-02-22)
|
|
8
|
+
=====================================================================================
|
|
9
|
+
|
|
10
|
+
New Features
|
|
11
|
+
------------
|
|
12
|
+
|
|
13
|
+
- calculate lts.ubuntu from current date (`2bfa12e <https://github.com/mzdun/proj-flow/commit/2bfa12e87cf7b5b163ef88473d2f4779afeed938>`_)
|
|
14
|
+
|
|
15
|
+
`0.12.0 <https://github.com/mzdun/proj-flow/compare/v0.11.3...v0.12.0>`_ (2025-02-22)
|
|
16
|
+
=====================================================================================
|
|
17
|
+
|
|
18
|
+
New Features
|
|
19
|
+
------------
|
|
20
|
+
|
|
21
|
+
- add ``github publish`` (`2f6ef1e <https://github.com/mzdun/proj-flow/commit/2f6ef1eaf4053f25633bf3f5037991fa4567023b>`_)
|
|
22
|
+
|
|
7
23
|
`0.11.3 <https://github.com/mzdun/proj-flow/compare/v0.11.2...v0.11.3>`_ (2025-02-19)
|
|
8
24
|
=====================================================================================
|
|
9
25
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: proj-flow
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.13.0
|
|
4
4
|
Summary: C++ project maintenance, automated
|
|
5
5
|
Project-URL: Changelog, https://github.com/mzdun/proj-flow/blob/main/CHANGELOG.rst
|
|
6
6
|
Project-URL: Documentation, https://proj-flow.readthedocs.io/en/latest/
|
|
@@ -56,6 +56,14 @@ class Project:
|
|
|
56
56
|
def tag_name(self):
|
|
57
57
|
return f"v{self.version}"
|
|
58
58
|
|
|
59
|
+
@property
|
|
60
|
+
def package_prefix(self):
|
|
61
|
+
return f"{self.archive_name}-"
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def package_suffix(self):
|
|
65
|
+
return ""
|
|
66
|
+
|
|
59
67
|
|
|
60
68
|
class ProjectSuite(ABC):
|
|
61
69
|
@abstractmethod
|
|
@@ -97,3 +105,13 @@ class ProjectSuite(ABC):
|
|
|
97
105
|
|
|
98
106
|
|
|
99
107
|
project_suites = registry.Registry[ProjectSuite]("ProjectSuite")
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def get_project(rt: env.Runtime):
|
|
111
|
+
def wrap(suite: ProjectSuite):
|
|
112
|
+
return suite.get_project(rt)
|
|
113
|
+
|
|
114
|
+
_, project = project_suites.find(wrap)
|
|
115
|
+
if project is None:
|
|
116
|
+
rt.fatal(f"Cannot get project information from {rt.root}")
|
|
117
|
+
return project
|
|
@@ -5,8 +5,10 @@
|
|
|
5
5
|
The **proj_flow.base.cmd** defines environment for `proj_flow.api.env.Runtime`.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
import os
|
|
8
9
|
import shutil
|
|
9
10
|
import subprocess
|
|
11
|
+
from contextlib import contextmanager
|
|
10
12
|
from typing import Optional
|
|
11
13
|
|
|
12
14
|
|
|
@@ -48,3 +50,13 @@ def run(app: str, *args: str, capture_output=False):
|
|
|
48
50
|
return subprocess.run(
|
|
49
51
|
[cmd, *args], shell=False, encoding="UTF-8", capture_output=capture_output
|
|
50
52
|
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@contextmanager
|
|
56
|
+
def cd(path: str):
|
|
57
|
+
prev = os.getcwd()
|
|
58
|
+
os.chdir(path)
|
|
59
|
+
try:
|
|
60
|
+
yield
|
|
61
|
+
finally:
|
|
62
|
+
os.chdir(prev)
|
|
@@ -5,12 +5,8 @@
|
|
|
5
5
|
The **proj_flow.ext.cplusplus.cmake.version** provides project suite plugin.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
import os
|
|
9
|
-
import re
|
|
10
8
|
from typing import NamedTuple, Optional
|
|
11
9
|
|
|
12
|
-
import toml
|
|
13
|
-
|
|
14
10
|
from proj_flow.api import env, release
|
|
15
11
|
from proj_flow.ext.cplusplus.cmake.parser import get_project
|
|
16
12
|
|
|
@@ -15,8 +15,9 @@ import sys
|
|
|
15
15
|
import typing
|
|
16
16
|
|
|
17
17
|
from proj_flow import log
|
|
18
|
-
from proj_flow.api import arg, env
|
|
18
|
+
from proj_flow.api import arg, env, release
|
|
19
19
|
from proj_flow.base.name_list import name_list
|
|
20
|
+
from proj_flow.ext.github import publishing
|
|
20
21
|
from proj_flow.flow.configs import Configs
|
|
21
22
|
from proj_flow.log import commit, hosting, rich_text
|
|
22
23
|
|
|
@@ -63,7 +64,7 @@ def matrix(
|
|
|
63
64
|
|
|
64
65
|
|
|
65
66
|
@arg.command("github", "release")
|
|
66
|
-
def
|
|
67
|
+
def release_cmd(
|
|
67
68
|
rt: env.Runtime,
|
|
68
69
|
all: typing.Annotated[
|
|
69
70
|
bool, arg.FlagArgument(help="Take all Conventional Commits.")
|
|
@@ -72,7 +73,7 @@ def release(
|
|
|
72
73
|
typing.Optional[str],
|
|
73
74
|
arg.Argument(
|
|
74
75
|
help="Ignore the version change from changelog and instead use this value. "
|
|
75
|
-
f"Allowed values are: {name_list(FORCED_LEVEL_CHOICES)}",
|
|
76
|
+
f"Allowed values are: {name_list(FORCED_LEVEL_CHOICES)}.",
|
|
76
77
|
meta="level",
|
|
77
78
|
choices=FORCED_LEVEL_CHOICES,
|
|
78
79
|
),
|
|
@@ -86,9 +87,9 @@ def release(
|
|
|
86
87
|
],
|
|
87
88
|
):
|
|
88
89
|
"""
|
|
89
|
-
|
|
90
|
-
commit for the change,
|
|
91
|
-
and
|
|
90
|
+
Bump the project version based on current git logs, create a "chore"
|
|
91
|
+
commit for the change, attach an annotated tag with the version number
|
|
92
|
+
and push it all to GitHub.
|
|
92
93
|
"""
|
|
93
94
|
|
|
94
95
|
generator = (
|
|
@@ -123,3 +124,62 @@ def release(
|
|
|
123
124
|
with open(GITHUB_OUTPUT, "a", encoding="UTF-8") as github_output:
|
|
124
125
|
print(f"tag={next_tag}", file=github_output)
|
|
125
126
|
print(f"released={json.dumps(released)}", file=github_output)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@arg.command("github", "publish")
|
|
130
|
+
def publish(
|
|
131
|
+
rt: env.Runtime,
|
|
132
|
+
ref: typing.Annotated[
|
|
133
|
+
typing.Optional[str],
|
|
134
|
+
arg.Argument(
|
|
135
|
+
help="Publish this release draft. In case this is called from within "
|
|
136
|
+
"GitHub Actions and your release is named exactly like the tag used to "
|
|
137
|
+
"trigger this flow, you can use ${{github.action_ref}} variable. "
|
|
138
|
+
"Defaults to current tag.",
|
|
139
|
+
meta="release",
|
|
140
|
+
),
|
|
141
|
+
],
|
|
142
|
+
upload: typing.Annotated[
|
|
143
|
+
typing.Optional[str],
|
|
144
|
+
arg.Argument(
|
|
145
|
+
help="If present, upload files from the directory to the referenced "
|
|
146
|
+
"release before publishing.",
|
|
147
|
+
meta="directory",
|
|
148
|
+
),
|
|
149
|
+
],
|
|
150
|
+
):
|
|
151
|
+
"""
|
|
152
|
+
Upload package artifacts to a GitHub release and in case the release
|
|
153
|
+
is still in draft, publish it.
|
|
154
|
+
"""
|
|
155
|
+
|
|
156
|
+
git = commit.Git(rt)
|
|
157
|
+
gh_links = hosting.github.GitHub.from_repo(git) or commit.NoHosting()
|
|
158
|
+
project = release.get_project(rt)
|
|
159
|
+
|
|
160
|
+
tag_name = ref or project.tag_name
|
|
161
|
+
|
|
162
|
+
release_info = gh_links.locate_release(tag_name)
|
|
163
|
+
if release_info is None and not rt.dry_run:
|
|
164
|
+
rt.fatal(f"No release matches {tag_name}")
|
|
165
|
+
|
|
166
|
+
if upload is not None:
|
|
167
|
+
matcher = publishing.build_regex(project)
|
|
168
|
+
directory, names = publishing.gather_artifacts(upload, matcher)
|
|
169
|
+
if not len(names):
|
|
170
|
+
rt.fatal(f"No artifact matches {matcher.pattern}")
|
|
171
|
+
|
|
172
|
+
publishing.checksums(rt, directory, names, "sha256sum.txt")
|
|
173
|
+
|
|
174
|
+
if release_info is not None:
|
|
175
|
+
gh_links.upload_to_release(release_info, directory, names)
|
|
176
|
+
else:
|
|
177
|
+
rt.message(f"Would upload:", level=env.Msg.STATUS)
|
|
178
|
+
for name in names:
|
|
179
|
+
rt.message(f" * {name}", level=env.Msg.STATUS)
|
|
180
|
+
|
|
181
|
+
if release_info is not None:
|
|
182
|
+
info = gh_links.publish(release_info)
|
|
183
|
+
if info.url:
|
|
184
|
+
msg = "Visit draft at" if info.is_draft else "Visit release at"
|
|
185
|
+
rt.message(msg, info.url, level=env.Msg.ALWAYS)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Copyright (c) 2025 Marcin Zdun
|
|
2
|
+
# This code is licensed under MIT license (see LICENSE for details)
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
The **proj_flow.ext.github.publishing** provides utilities for ``github
|
|
6
|
+
publish`` command.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
import hashlib
|
|
11
|
+
import io
|
|
12
|
+
import os
|
|
13
|
+
import re
|
|
14
|
+
import typing
|
|
15
|
+
import zipfile
|
|
16
|
+
|
|
17
|
+
from proj_flow.api import env, release
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _safe_regex(value: str) -> str:
|
|
21
|
+
for esc in "\\.+*?()[]":
|
|
22
|
+
value = value.replace(esc, f"\\{esc}")
|
|
23
|
+
return value
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def build_regex(project: release.Project):
|
|
27
|
+
regexPre = _safe_regex(project.package_prefix)
|
|
28
|
+
regexPost = _safe_regex(project.package_suffix)
|
|
29
|
+
regex = f"^{regexPre}(.*){regexPost}$"
|
|
30
|
+
return re.compile(regex)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def gather_artifacts(directory: str, matcher: re.Pattern):
|
|
34
|
+
if os.path.isdir(directory):
|
|
35
|
+
for _, dirnames, filenames in os.walk(directory):
|
|
36
|
+
dirnames[:] = []
|
|
37
|
+
names = [name for name in filenames if matcher.match(name)]
|
|
38
|
+
else:
|
|
39
|
+
next_directory = f"{directory}-dir"
|
|
40
|
+
os.makedirs(next_directory, exist_ok=True)
|
|
41
|
+
with zipfile.ZipFile(directory) as zip:
|
|
42
|
+
names = [name for name in zip.namelist() if matcher.match(name)]
|
|
43
|
+
for name in names:
|
|
44
|
+
zip.extract(name, path=next_directory)
|
|
45
|
+
directory = next_directory
|
|
46
|
+
|
|
47
|
+
return directory, names
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _hash(filename: str) -> str:
|
|
51
|
+
sha = hashlib.sha256()
|
|
52
|
+
with open(filename, "rb") as data:
|
|
53
|
+
for block in iter(lambda: data.read(io.DEFAULT_BUFFER_SIZE), b""):
|
|
54
|
+
sha.update(block)
|
|
55
|
+
return sha.hexdigest()
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def checksums(rt: env.Runtime, directory: str, names: typing.List[str], outname: str):
|
|
59
|
+
rt.print("sha256sum", "-b")
|
|
60
|
+
if not rt.dry_run:
|
|
61
|
+
with open(os.path.join(directory, outname), "w") as output:
|
|
62
|
+
for name in names:
|
|
63
|
+
digest = _hash(os.path.join(directory, name))
|
|
64
|
+
print(f"{digest} *{name}", file=output)
|
|
65
|
+
names.append(outname)
|
|
@@ -15,6 +15,7 @@ from contextlib import contextmanager
|
|
|
15
15
|
from typing import Any, Callable, Dict, List, Optional, cast
|
|
16
16
|
|
|
17
17
|
from proj_flow.api import env, step
|
|
18
|
+
from proj_flow.base import cmd
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
@step.register
|
|
@@ -172,16 +173,6 @@ _job_listing = [
|
|
|
172
173
|
]
|
|
173
174
|
|
|
174
175
|
|
|
175
|
-
@contextmanager
|
|
176
|
-
def _cd(path: str):
|
|
177
|
-
prev = os.getcwd()
|
|
178
|
-
os.chdir(path)
|
|
179
|
-
try:
|
|
180
|
-
yield
|
|
181
|
-
finally:
|
|
182
|
-
os.chdir(prev)
|
|
183
|
-
|
|
184
|
-
|
|
185
176
|
def _get_venv_path(root: str):
|
|
186
177
|
bindir = os.path.join(".venv", "bin")
|
|
187
178
|
scripts = os.path.join(".venv", "Scripts")
|
|
@@ -198,7 +189,7 @@ def _get_venv_path(root: str):
|
|
|
198
189
|
def _activate_virtual_env(venv, root: str):
|
|
199
190
|
global PYTHON_EXECUTABLE
|
|
200
191
|
|
|
201
|
-
with
|
|
192
|
+
with cmd.cd(root):
|
|
202
193
|
exec_ext = ".exe" if sys.platform == "win32" else ""
|
|
203
194
|
python_exec = f"python{exec_ext}"
|
|
204
195
|
bindir = _get_venv_path(root)
|
|
@@ -25,13 +25,6 @@ def _package_name(config: env.Config, pkg: str, group: str):
|
|
|
25
25
|
return f"{pkg}-{_system}{_version}-{_arch}{debug}{suffix}"
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
def _get_project(rt: env.Runtime):
|
|
29
|
-
def wrap(suite: release.ProjectSuite):
|
|
30
|
-
return suite.get_project(rt)
|
|
31
|
-
|
|
32
|
-
return wrap
|
|
33
|
-
|
|
34
|
-
|
|
35
28
|
@step.register
|
|
36
29
|
class StorePackages(step.Step):
|
|
37
30
|
"""Stores archives and installers build for ``preset`` config value."""
|
|
@@ -52,10 +45,7 @@ class StorePackages(step.Step):
|
|
|
52
45
|
|
|
53
46
|
global _project_pkg
|
|
54
47
|
if _project_pkg is None:
|
|
55
|
-
|
|
56
|
-
if project is None:
|
|
57
|
-
rt.fatal(f"Cannot get project information from {rt.root}")
|
|
58
|
-
_project_pkg = project.archive_name
|
|
48
|
+
_project_pkg = release.get_project(rt).archive_name
|
|
59
49
|
|
|
60
50
|
main_group = cast(str, rt._cfg.get("package", {}).get("main-group"))
|
|
61
51
|
if main_group is not None and not rt.dry_run:
|
|
@@ -9,7 +9,9 @@ using ``-D`` switches.
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
import argparse
|
|
12
|
+
import datetime
|
|
12
13
|
import os
|
|
14
|
+
import sys
|
|
13
15
|
from typing import Any, Callable, Dict, List
|
|
14
16
|
|
|
15
17
|
from proj_flow.api import env
|
|
@@ -90,9 +92,43 @@ def _expand_one(config: dict, github_os: str, os_in_name: str):
|
|
|
90
92
|
return config
|
|
91
93
|
|
|
92
94
|
|
|
95
|
+
__printed_lts_ubuntu_warning = False
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _ubuntu_lts(today=datetime.date.today(), lts_years=5):
|
|
99
|
+
year = today.year
|
|
100
|
+
for y in range(year - lts_years, year + 1):
|
|
101
|
+
if y % 2 != 0:
|
|
102
|
+
continue
|
|
103
|
+
release = datetime.date(y, 4, 1)
|
|
104
|
+
end_of_life = datetime.date(y + lts_years, 1, 31)
|
|
105
|
+
if release > today or end_of_life < today:
|
|
106
|
+
continue
|
|
107
|
+
yield f"ubuntu-{y % 100}.04"
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _lts_list(config: dict, lts_list: Dict[str, List[str]]):
|
|
111
|
+
os = config["os"]
|
|
112
|
+
raw = lts_list.get(os)
|
|
113
|
+
if os == "ubuntu":
|
|
114
|
+
if raw is not None:
|
|
115
|
+
global __printed_lts_ubuntu_warning
|
|
116
|
+
if not __printed_lts_ubuntu_warning:
|
|
117
|
+
__printed_lts_ubuntu_warning = True
|
|
118
|
+
print(
|
|
119
|
+
"\033[1;33m-- lts.ubuntu in config.yaml is deprecated; "
|
|
120
|
+
"please remove it, so it can be calculated base on "
|
|
121
|
+
"current date\033[m",
|
|
122
|
+
file=sys.stderr,
|
|
123
|
+
)
|
|
124
|
+
else:
|
|
125
|
+
raw = list(_ubuntu_lts())
|
|
126
|
+
return raw or []
|
|
127
|
+
|
|
128
|
+
|
|
93
129
|
def _expand_config(config: dict, spread_lts: bool, lts_list: Dict[str, List[str]]):
|
|
94
130
|
if spread_lts:
|
|
95
|
-
spread =
|
|
131
|
+
spread = _lts_list(config, lts_list)
|
|
96
132
|
if len(spread):
|
|
97
133
|
return [
|
|
98
134
|
_expand_one({key: config[key] for key in config}, lts, lts)
|
|
@@ -136,6 +172,11 @@ class Configs:
|
|
|
136
172
|
# from commands/github
|
|
137
173
|
spread_lts = hasattr(args, "matrix") and not not args.matrix
|
|
138
174
|
|
|
175
|
+
if not spread_lts:
|
|
176
|
+
# allow "run" to see the warning about "lts.ubuntu"
|
|
177
|
+
for config in configs:
|
|
178
|
+
_lts_list(config, rt.lts_list)
|
|
179
|
+
|
|
139
180
|
turned = matrix.flatten(
|
|
140
181
|
[
|
|
141
182
|
_expand_config(config, spread_lts, rt.lts_list)
|
|
@@ -188,7 +188,10 @@ def _sem_ver(tag: str):
|
|
|
188
188
|
|
|
189
189
|
@dataclass
|
|
190
190
|
class ReleaseInfo:
|
|
191
|
-
|
|
191
|
+
url: Optional[str] = None
|
|
192
|
+
is_draft: Optional[bool] = None
|
|
193
|
+
ref: Optional[str] = None
|
|
194
|
+
tag: Optional[str] = None
|
|
192
195
|
|
|
193
196
|
|
|
194
197
|
class Hosting(ABC):
|
|
@@ -241,6 +244,32 @@ class Hosting(ABC):
|
|
|
241
244
|
"""
|
|
242
245
|
...
|
|
243
246
|
|
|
247
|
+
@abstractmethod
|
|
248
|
+
def locate_release(self, release_name: str) -> Optional[ReleaseInfo]:
|
|
249
|
+
"""
|
|
250
|
+
Locate a release by its name.
|
|
251
|
+
"""
|
|
252
|
+
...
|
|
253
|
+
|
|
254
|
+
@abstractmethod
|
|
255
|
+
def upload_to_release(
|
|
256
|
+
self,
|
|
257
|
+
release: ReleaseInfo,
|
|
258
|
+
directory: str,
|
|
259
|
+
names: list[str],
|
|
260
|
+
):
|
|
261
|
+
"""
|
|
262
|
+
Upload package artifacts to the release.
|
|
263
|
+
"""
|
|
264
|
+
...
|
|
265
|
+
|
|
266
|
+
@abstractmethod
|
|
267
|
+
def publish(self, release: ReleaseInfo) -> ReleaseInfo:
|
|
268
|
+
"""
|
|
269
|
+
Publish given release, return updated release info.
|
|
270
|
+
"""
|
|
271
|
+
...
|
|
272
|
+
|
|
244
273
|
|
|
245
274
|
class NoHosting(Hosting):
|
|
246
275
|
"""
|
|
@@ -266,7 +295,21 @@ class NoHosting(Hosting):
|
|
|
266
295
|
def add_release(
|
|
267
296
|
self, log: ChangeLog, setup: "LogSetup", git: "Git", draft: bool
|
|
268
297
|
) -> ReleaseInfo:
|
|
269
|
-
return ReleaseInfo(
|
|
298
|
+
return ReleaseInfo(is_draft=False)
|
|
299
|
+
|
|
300
|
+
def locate_release(self, release_name: str) -> Optional[ReleaseInfo]:
|
|
301
|
+
return None
|
|
302
|
+
|
|
303
|
+
def upload_to_release(
|
|
304
|
+
self,
|
|
305
|
+
release: ReleaseInfo,
|
|
306
|
+
directory: str,
|
|
307
|
+
names: list[str],
|
|
308
|
+
):
|
|
309
|
+
return None
|
|
310
|
+
|
|
311
|
+
def publish(self, release: ReleaseInfo):
|
|
312
|
+
return ReleaseInfo(is_draft=False)
|
|
270
313
|
|
|
271
314
|
|
|
272
315
|
@dataclass
|
|
@@ -23,6 +23,21 @@ class _GitHub(NamedTuple):
|
|
|
23
23
|
_NO_GITHUB = _GitHub("", "", "")
|
|
24
24
|
|
|
25
25
|
|
|
26
|
+
class ReleaseInfo(commit.ReleaseInfo):
|
|
27
|
+
id: int
|
|
28
|
+
|
|
29
|
+
def __init__(
|
|
30
|
+
self,
|
|
31
|
+
url: Optional[str],
|
|
32
|
+
is_draft: Optional[bool],
|
|
33
|
+
id: int,
|
|
34
|
+
ref: Optional[str] = None,
|
|
35
|
+
tag: Optional[str] = None,
|
|
36
|
+
):
|
|
37
|
+
super().__init__(url, is_draft, ref, tag)
|
|
38
|
+
self.id = id
|
|
39
|
+
|
|
40
|
+
|
|
26
41
|
class GitHub(commit.Hosting):
|
|
27
42
|
"""
|
|
28
43
|
Generates links to GitHub.
|
|
@@ -78,6 +93,7 @@ class GitHub(commit.Hosting):
|
|
|
78
93
|
*args: str,
|
|
79
94
|
method: Optional[str] = None,
|
|
80
95
|
capture_output: bool = True,
|
|
96
|
+
ro_call: bool = False,
|
|
81
97
|
**kwargs,
|
|
82
98
|
):
|
|
83
99
|
url = f"{self.root}{res}"
|
|
@@ -94,15 +110,18 @@ class GitHub(commit.Hosting):
|
|
|
94
110
|
call.extend(["--method", method.upper()])
|
|
95
111
|
|
|
96
112
|
if capture_output:
|
|
97
|
-
if self.rt.dry_run:
|
|
113
|
+
if not ro_call and self.rt.dry_run:
|
|
98
114
|
self.rt.print(*call, url, *args)
|
|
99
115
|
return None
|
|
100
116
|
return self.rt.capture(*call, url, *args)
|
|
101
117
|
|
|
102
|
-
self.
|
|
118
|
+
return self._run(*call, url, *args)
|
|
119
|
+
|
|
120
|
+
def _run(self, *args: str):
|
|
121
|
+
self.rt.print(*args)
|
|
103
122
|
if self.rt.dry_run:
|
|
104
123
|
return None
|
|
105
|
-
return cmd.run(*
|
|
124
|
+
return cmd.run(*args)
|
|
106
125
|
|
|
107
126
|
def json_from(
|
|
108
127
|
self,
|
|
@@ -111,8 +130,9 @@ class GitHub(commit.Hosting):
|
|
|
111
130
|
method: Optional[str] = None,
|
|
112
131
|
server: Optional[str] = None,
|
|
113
132
|
default: Any = {},
|
|
133
|
+
ro_call: bool = False,
|
|
114
134
|
):
|
|
115
|
-
proc = self.gh(res, *args, method=method, server=server)
|
|
135
|
+
proc = self.gh(res, *args, method=method, server=server, ro_call=ro_call)
|
|
116
136
|
if proc is None:
|
|
117
137
|
return default
|
|
118
138
|
|
|
@@ -131,6 +151,15 @@ class GitHub(commit.Hosting):
|
|
|
131
151
|
|
|
132
152
|
return json.loads(proc.stdout)
|
|
133
153
|
|
|
154
|
+
def _release_from_json(self, data: dict, draft: bool = False):
|
|
155
|
+
html_url = cast(Optional[str], data.get("html_url"))
|
|
156
|
+
draft = cast(bool, data.get("draft", draft))
|
|
157
|
+
id = cast(int, data.get("id", 0))
|
|
158
|
+
name = cast(str, data.get("name"))
|
|
159
|
+
tag_name = cast(str, data.get("tag_name"))
|
|
160
|
+
|
|
161
|
+
return ReleaseInfo(url=html_url, is_draft=draft, id=id, ref=name, tag=tag_name)
|
|
162
|
+
|
|
134
163
|
def add_release(
|
|
135
164
|
self,
|
|
136
165
|
log: commit.ChangeLog,
|
|
@@ -162,9 +191,46 @@ class GitHub(commit.Hosting):
|
|
|
162
191
|
flags.append(f"{name}={value}")
|
|
163
192
|
|
|
164
193
|
data = self.json_from("/releases", *flags, method="POST", default={})
|
|
165
|
-
|
|
194
|
+
return self._release_from_json(data, draft)
|
|
166
195
|
|
|
167
|
-
|
|
196
|
+
def locate_release(self, release_name: str) -> Optional[commit.ReleaseInfo]:
|
|
197
|
+
releases = self.json_from("/releases", default=[], ro_call=True)
|
|
198
|
+
for release in releases:
|
|
199
|
+
if release.get("name") == release_name:
|
|
200
|
+
return self._release_from_json(release)
|
|
201
|
+
|
|
202
|
+
return None
|
|
203
|
+
|
|
204
|
+
def upload_to_release(
|
|
205
|
+
self,
|
|
206
|
+
release: commit.ReleaseInfo,
|
|
207
|
+
directory: str,
|
|
208
|
+
names: list[str],
|
|
209
|
+
):
|
|
210
|
+
with cmd.cd(directory):
|
|
211
|
+
return self._run(
|
|
212
|
+
"gh",
|
|
213
|
+
"release",
|
|
214
|
+
"upload",
|
|
215
|
+
release.tag or release.ref or "",
|
|
216
|
+
*names,
|
|
217
|
+
"--clobber",
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
def publish(self, release: commit.ReleaseInfo) -> commit.ReleaseInfo:
|
|
221
|
+
if not isinstance(release, ReleaseInfo):
|
|
222
|
+
return commit.ReleaseInfo(is_draft=False)
|
|
223
|
+
|
|
224
|
+
release_id = release.id
|
|
225
|
+
data = self.json_from(
|
|
226
|
+
f"/releases/{release_id}",
|
|
227
|
+
"-f",
|
|
228
|
+
"draft=false",
|
|
229
|
+
"-F",
|
|
230
|
+
"make_latest=legacy",
|
|
231
|
+
method="PATCH",
|
|
232
|
+
)
|
|
233
|
+
return self._release_from_json(data)
|
|
168
234
|
|
|
169
235
|
@staticmethod
|
|
170
236
|
def from_repo(git: commit.Git, remote: Optional[str] = None):
|
|
@@ -130,8 +130,9 @@ def add_release(
|
|
|
130
130
|
git.annotated_tag(setup.curr_tag, commit_message)
|
|
131
131
|
|
|
132
132
|
if hosting.is_active:
|
|
133
|
-
|
|
134
|
-
if
|
|
135
|
-
|
|
133
|
+
info = hosting.add_release(changelog, setup, git, draft)
|
|
134
|
+
if info.url:
|
|
135
|
+
msg = "Visit draft at" if info.is_draft else "Visit release at"
|
|
136
|
+
rt.message(msg, info.url, level=env.Msg.ALWAYS)
|
|
136
137
|
|
|
137
138
|
return setup.curr_tag
|
|
@@ -18,17 +18,9 @@ compiler:
|
|
|
18
18
|
gcc: [ gcc, g++ ]
|
|
19
19
|
os-default: { ubuntu: gcc, windows: msvc }
|
|
20
20
|
|
|
21
|
-
lts:
|
|
22
|
-
ubuntu:
|
|
23
|
-
- ubuntu-20.04
|
|
24
|
-
- ubuntu-22.04
|
|
25
|
-
- ubuntu-24.04
|
|
26
|
-
|
|
27
21
|
postproc:
|
|
28
22
|
exclude:
|
|
29
|
-
- { github_os: ubuntu-20.04, sanitizer: true }
|
|
30
23
|
- { github_os: ubuntu-24.04, sanitizer: true }
|
|
31
|
-
- { github_os: ubuntu-20.04, compiler: clang }
|
|
32
24
|
|
|
33
25
|
|
|
34
26
|
shortcuts:
|