mirror of
https://github.com/arcan1s/ahriman.git
synced 2026-07-13 22:31:07 +00:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e2e013a33b | |||
| da0853b6f1 | |||
| 7db38ff9e4 | |||
| 92ac036f78 | |||
| aef7fa5f93 | |||
| c412b08135 | |||
| a3b6372c11 | |||
| 78288befb8 | |||
| 49cd3d43c8 | |||
| d9b52806c0 | |||
| 774db2d780 | |||
| 68afda4c71 | |||
| fa9fa73078 | |||
| 3e1e24cb50 | |||
| 18fe38c30b | |||
| 6ee8b26bd5 | |||
| fce49f22c9 | |||
| af8e2c9e9b | |||
| 1c312bb528 | |||
| e39194e9f6 | |||
| 21cc029c18 | |||
| 40671b99d5 | |||
| 3ad2c494af | |||
| 34014d1cdd | |||
| 93ed2b864b | |||
| cca931ccd0 | |||
| 5e090cebdb | |||
| d7984c12f0 | |||
| 96684d7ddd |
@@ -26,6 +26,10 @@ jobs:
|
||||
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Set image date
|
||||
id: args
|
||||
run: echo "::set-output name=date::$(date -d yesterday +'%Y-%m-%d')"
|
||||
|
||||
- name: Login to docker hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
@@ -53,6 +57,8 @@ jobs:
|
||||
- name: Build an image and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
build-args: |
|
||||
BUILD_DATE=${{ steps.args.outputs.date }}
|
||||
file: docker/Dockerfile
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
|
||||
@@ -12,11 +12,11 @@ pacman -Syyu --noconfirm
|
||||
# main dependencies
|
||||
pacman -S --noconfirm devtools git npm pyalpm python-bcrypt python-filelock python-inflection python-pyelftools python-requests python-systemd sudo
|
||||
# make dependencies
|
||||
pacman -S --noconfirm --asdeps base-devel python-build python-flit python-installer python-tox python-wheel
|
||||
pacman -S --noconfirm --asdeps base-devel python-build python-hatchling python-installer python-tox python-wheel
|
||||
# optional dependencies
|
||||
if [[ -z $MINIMAL_INSTALL ]]; then
|
||||
# web server
|
||||
pacman -S --noconfirm python-aioauth-client python-aiohttp python-aiohttp-apispec-git python-aiohttp-cors python-aiohttp-jinja2 python-aiohttp-security python-aiohttp-session python-cryptography python-jinja
|
||||
pacman -S --noconfirm python-aioauth-client python-aiohttp python-aiohttp-apispec-git python-aiohttp-cors python-aiohttp-jinja2 python-aiohttp-security python-aiohttp-session python-aiohttp-sse-git python-cryptography python-jinja
|
||||
# additional features
|
||||
pacman -S --noconfirm gnupg ipython python-boto3 python-cerberus python-matplotlib rsync
|
||||
fi
|
||||
|
||||
+5
-2
@@ -103,5 +103,8 @@ docs/html/
|
||||
# Frontend
|
||||
node_modules/
|
||||
package-lock.json
|
||||
package/share/ahriman/templates/static/index.js
|
||||
package/share/ahriman/templates/static/index.css
|
||||
ahriman-web/package/share/ahriman/templates/static/index.js
|
||||
ahriman-web/package/share/ahriman/templates/static/index.css
|
||||
|
||||
# local configs
|
||||
/*.ini
|
||||
|
||||
@@ -2,4 +2,6 @@
|
||||
addopts = --cov=ahriman --cov-report=term-missing:skip-covered --no-cov-on-fail --cov-fail-under=100 --spec
|
||||
asyncio_default_fixture_loop_scope = function
|
||||
asyncio_mode = auto
|
||||
pythonpath = tests
|
||||
resource-path.directory-name-test-resources = ../../tests/testresources
|
||||
spec_test_format = {result} {docstring_summary}
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
version: 2
|
||||
|
||||
build:
|
||||
os: ubuntu-20.04
|
||||
os: ubuntu-lts-latest
|
||||
tools:
|
||||
python: "3.12"
|
||||
python: "3.13"
|
||||
apt_packages:
|
||||
- graphviz
|
||||
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "ahriman-core"
|
||||
description = "ArcH linux ReposItory MANager, core package"
|
||||
readme = "../README.md"
|
||||
requires-python = ">=3.13"
|
||||
license = {file = "../COPYING"}
|
||||
authors = [
|
||||
{name = "ahriman team"},
|
||||
]
|
||||
dependencies = [
|
||||
"bcrypt",
|
||||
"filelock",
|
||||
"inflection",
|
||||
"pyelftools",
|
||||
"requests",
|
||||
]
|
||||
dynamic = ["version"]
|
||||
|
||||
[project.optional-dependencies]
|
||||
journald = [
|
||||
"systemd-python",
|
||||
]
|
||||
# FIXME technically this dependency is required, but in some cases we do not have access to
|
||||
# the libalpm which is required in order to install the package. Thus in case if we do not
|
||||
# really need to run the application we can move it to "optional" dependencies
|
||||
pacman = [
|
||||
"pyalpm",
|
||||
]
|
||||
reports = [
|
||||
"Jinja2",
|
||||
]
|
||||
s3 = [
|
||||
"boto3",
|
||||
]
|
||||
shell = [
|
||||
"IPython",
|
||||
]
|
||||
stats = [
|
||||
"matplotlib",
|
||||
]
|
||||
unixsocket = [
|
||||
"requests-unixsocket2",
|
||||
]
|
||||
validator = [
|
||||
"cerberus",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
ahriman = "ahriman.application.ahriman:run"
|
||||
|
||||
[project.urls]
|
||||
Documentation = "https://ahriman.readthedocs.io/"
|
||||
Repository = "https://github.com/arcan1s/ahriman"
|
||||
Changelog = "https://github.com/arcan1s/ahriman/releases"
|
||||
|
||||
[tool.hatch.version]
|
||||
path = "src/ahriman/__init__.py"
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ["src/ahriman"]
|
||||
|
||||
[tool.hatch.build.targets.wheel.shared-data]
|
||||
"package/lib" = "lib"
|
||||
"package/share" = "share"
|
||||
+5
-2
@@ -156,12 +156,15 @@ class ApplicationRepository(ApplicationProperties):
|
||||
result = Result()
|
||||
|
||||
# process already built packages if any
|
||||
built_packages = self.repository.packages_built()
|
||||
if built_packages: # speedup a bit
|
||||
if built_packages := self.repository.packages_built(): # speedup a bit
|
||||
build_result = self.repository.process_update(built_packages, packagers)
|
||||
self.on_result(build_result)
|
||||
result.merge(build_result)
|
||||
|
||||
# filter packages which were prebuilt
|
||||
succeeded = {package.base for package in build_result.success}
|
||||
updates = [package for package in updates if package.base not in succeeded]
|
||||
|
||||
builder = Updater.load(self.repository_id, self.configuration, self.repository)
|
||||
|
||||
# ok so for now we split all packages into chunks and process each chunk accordingly
|
||||
+25
-24
@@ -21,10 +21,10 @@ import argparse
|
||||
|
||||
from ahriman.application.application import Application
|
||||
from ahriman.application.handlers.handler import Handler, SubParserAction
|
||||
from ahriman.application.handlers.update import Update
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.utils import enum_values, extract_user
|
||||
from ahriman.models.package_source import PackageSource
|
||||
from ahriman.models.packagers import Packagers
|
||||
from ahriman.models.pkgbuild_patch import PkgbuildPatch
|
||||
from ahriman.models.repository_id import RepositoryId
|
||||
|
||||
@@ -48,26 +48,7 @@ class Add(Handler):
|
||||
"""
|
||||
application = Application(repository_id, configuration, report=report, refresh_pacman_database=args.refresh)
|
||||
application.on_start()
|
||||
|
||||
application.add(args.package, args.source, args.username)
|
||||
patches = [PkgbuildPatch.from_env(patch) for patch in args.variable] if args.variable is not None else []
|
||||
for package in args.package: # for each requested package insert patch
|
||||
for patch in patches:
|
||||
application.reporter.package_patches_update(package, patch)
|
||||
|
||||
if not args.now:
|
||||
return
|
||||
|
||||
packages = application.updates(args.package, aur=False, local=False, manual=True, vcs=False, check_files=False)
|
||||
if args.changes: # generate changes if requested
|
||||
application.changes(packages)
|
||||
|
||||
packages = application.with_dependencies(packages, process_dependencies=args.dependencies)
|
||||
packagers = Packagers(args.username, {package.base: package.packager for package in packages})
|
||||
|
||||
application.print_updates(packages, log_fn=application.logger.info)
|
||||
result = application.update(packages, packagers, bump_pkgrel=args.increment)
|
||||
Add.check_status(args.exit_code, not result.is_empty)
|
||||
Add.perform_action(application, args)
|
||||
|
||||
@staticmethod
|
||||
def _set_package_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
@@ -103,14 +84,34 @@ class Add(Handler):
|
||||
parser.add_argument("--increment", help="increment package release (pkgrel) version on duplicate",
|
||||
action=argparse.BooleanOptionalAction, default=True)
|
||||
parser.add_argument("-n", "--now", help="run update function after", action="store_true")
|
||||
parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
|
||||
"-yy to force refresh even if up to date",
|
||||
action="count", default=False)
|
||||
parser.add_argument("-s", "--source", help="explicitly specify the package source for this command",
|
||||
type=PackageSource, choices=enum_values(PackageSource), default=PackageSource.Auto)
|
||||
parser.add_argument("-u", "--username", help="build as user", default=extract_user())
|
||||
parser.add_argument("-v", "--variable", help="apply specified makepkg variables to the next build",
|
||||
action="append")
|
||||
parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
|
||||
"-yy to force refresh even if up to date",
|
||||
action="count", default=False)
|
||||
parser.set_defaults(aur=False, check_files=False, dry_run=False, local=False, manual=True, vcs=False)
|
||||
return parser
|
||||
|
||||
@staticmethod
|
||||
def perform_action(application: Application, args: argparse.Namespace) -> None:
|
||||
"""
|
||||
perform add action
|
||||
|
||||
Args:
|
||||
application(Application): application instance
|
||||
args(argparse.Namespace): command line args
|
||||
"""
|
||||
application.add(args.package, args.source, args.username)
|
||||
patches = [PkgbuildPatch.from_env(patch) for patch in args.variable] if args.variable is not None else []
|
||||
for package in args.package: # for each requested package insert patch
|
||||
for patch in patches:
|
||||
application.reporter.package_patches_update(package, patch)
|
||||
|
||||
if not args.now:
|
||||
return
|
||||
Update.perform_action(application, args)
|
||||
|
||||
arguments = [_set_package_add_parser]
|
||||
@@ -0,0 +1,131 @@
|
||||
#
|
||||
# Copyright (c) 2021-2026 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# 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, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import argparse
|
||||
|
||||
from dataclasses import replace
|
||||
from pathlib import Path
|
||||
|
||||
from ahriman.application.application import Application
|
||||
from ahriman.application.handlers.add import Add
|
||||
from ahriman.application.handlers.handler import Handler, SubParserAction
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.exceptions import UnknownPackageError
|
||||
from ahriman.core.utils import extract_user
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.package_source import PackageSource
|
||||
from ahriman.models.repository_id import RepositoryId
|
||||
|
||||
|
||||
class Rollback(Handler):
|
||||
"""
|
||||
package rollback handler
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *,
|
||||
report: bool) -> None:
|
||||
"""
|
||||
callback for command line
|
||||
|
||||
Args:
|
||||
args(argparse.Namespace): command line args
|
||||
repository_id(RepositoryId): repository unique identifier
|
||||
configuration(Configuration): configuration instance
|
||||
report(bool): force enable or disable reporting
|
||||
"""
|
||||
application = Application(repository_id, configuration, report=report)
|
||||
application.on_start()
|
||||
|
||||
package = Rollback.package_load(application, args.package, args.version)
|
||||
artifacts = Rollback.package_artifacts(application, package)
|
||||
|
||||
args.package = [str(artifact) for artifact in artifacts]
|
||||
Add.perform_action(application, args)
|
||||
|
||||
if args.hold:
|
||||
application.reporter.package_hold_update(package.base, enabled=True)
|
||||
|
||||
@staticmethod
|
||||
def _set_package_rollback_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
"""
|
||||
add parser for package rollback subcommand
|
||||
|
||||
Args:
|
||||
root(SubParserAction): subparsers for the commands
|
||||
|
||||
Returns:
|
||||
argparse.ArgumentParser: created argument parser
|
||||
"""
|
||||
parser = root.add_parser("package-rollback", help="rollback package",
|
||||
description="rollback package to specified version from archives")
|
||||
parser.add_argument("package", help="package base")
|
||||
parser.add_argument("version", help="package version")
|
||||
parser.add_argument("--hold", help="hold package afterwards",
|
||||
action=argparse.BooleanOptionalAction, default=True)
|
||||
parser.add_argument("-u", "--username", help="build as user", default=extract_user())
|
||||
parser.set_defaults(aur=False, changes=False, check_files=False, dependencies=False, dry_run=False,
|
||||
exit_code=True, increment=False, now=True, local=False, manual=False, refresh=False,
|
||||
source=PackageSource.Archive, variable=None, vcs=False)
|
||||
return parser
|
||||
|
||||
@staticmethod
|
||||
def package_artifacts(application: Application, package: Package) -> list[Path]:
|
||||
"""
|
||||
look for requested package artifacts and return paths to them
|
||||
|
||||
Args:
|
||||
application(Application): application instance
|
||||
package(Package): package descriptor
|
||||
|
||||
Returns:
|
||||
list[Path]: paths to found artifacts
|
||||
|
||||
Raises:
|
||||
UnknownPackageError: if artifacts do not exist
|
||||
"""
|
||||
# lookup for built artifacts
|
||||
artifacts = application.repository.package_archives_lookup(package)
|
||||
if not artifacts:
|
||||
raise UnknownPackageError(package.base)
|
||||
return artifacts
|
||||
|
||||
@staticmethod
|
||||
def package_load(application: Application, package_base: str, version: str) -> Package:
|
||||
"""
|
||||
load package from repository, while setting requested version
|
||||
|
||||
Args:
|
||||
application(Application): application instance
|
||||
package_base(str): package base
|
||||
version(str): package version
|
||||
|
||||
Returns:
|
||||
Package: loaded package
|
||||
|
||||
Raises:
|
||||
UnknownPackageError: if package does not exist
|
||||
"""
|
||||
try:
|
||||
package, _ = next(iter(application.reporter.package_get(package_base)))
|
||||
return replace(package, version=version)
|
||||
except StopIteration:
|
||||
raise UnknownPackageError(package_base) from None
|
||||
|
||||
arguments = [_set_package_rollback_parser]
|
||||
+26
-16
@@ -48,22 +48,7 @@ class Update(Handler):
|
||||
"""
|
||||
application = Application(repository_id, configuration, report=report, refresh_pacman_database=args.refresh)
|
||||
application.on_start()
|
||||
|
||||
packages = application.updates(args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs,
|
||||
check_files=args.check_files)
|
||||
if args.changes: # generate changes if requested
|
||||
application.changes(packages)
|
||||
|
||||
if args.dry_run: # exit from application if no build requested
|
||||
Update.check_status(args.exit_code, packages) # status code check
|
||||
return
|
||||
|
||||
packages = application.with_dependencies(packages, process_dependencies=args.dependencies)
|
||||
packagers = Packagers(args.username, {package.base: package.packager for package in packages})
|
||||
|
||||
application.print_updates(packages, log_fn=application.logger.info)
|
||||
result = application.update(packages, packagers, bump_pkgrel=args.increment)
|
||||
Update.check_status(args.exit_code, not result.is_empty)
|
||||
Update.perform_action(application, args)
|
||||
|
||||
@staticmethod
|
||||
def _set_repo_check_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
@@ -153,6 +138,31 @@ class Update(Handler):
|
||||
return print(line) if dry_run else application.logger.info(line) # pylint: disable=bad-builtin
|
||||
return inner
|
||||
|
||||
@staticmethod
|
||||
def perform_action(application: Application, args: argparse.Namespace) -> None:
|
||||
"""
|
||||
perform update action
|
||||
|
||||
Args:
|
||||
application(Application): application instance
|
||||
args(argparse.Namespace): command line args
|
||||
"""
|
||||
packages = application.updates(args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs,
|
||||
check_files=args.check_files)
|
||||
if args.changes: # generate changes if requested
|
||||
application.changes(packages)
|
||||
|
||||
if args.dry_run: # exit from application if no build requested
|
||||
Update.check_status(args.exit_code, packages) # status code check
|
||||
return
|
||||
|
||||
packages = application.with_dependencies(packages, process_dependencies=args.dependencies)
|
||||
packagers = Packagers(args.username, {package.base: package.packager for package in packages})
|
||||
|
||||
application.print_updates(packages, log_fn=application.logger.info)
|
||||
result = application.update(packages, packagers, bump_pkgrel=args.increment)
|
||||
Update.check_status(args.exit_code, not result.is_empty)
|
||||
|
||||
arguments = [
|
||||
_set_repo_check_parser,
|
||||
_set_repo_update_parser,
|
||||
+1
-1
@@ -104,7 +104,7 @@ class PkgbuildParser(shlex.shlex):
|
||||
|
||||
# ignore substitution and extend bash symbols
|
||||
self.wordchars += "${}#:+-@!"
|
||||
# in case of default behaviour, it will ignore, for example, segment part of url outside of quotes
|
||||
# in case of default behavior, it will ignore, for example, segment part of url outside of quotes
|
||||
self.commenters = ""
|
||||
|
||||
@staticmethod
|
||||
+1
-1
@@ -72,7 +72,7 @@ class AUR(Remote):
|
||||
parse RPC response to package list
|
||||
|
||||
Args:
|
||||
response(dict[str, Any]): RPC response json
|
||||
response(dict[str, Any]): RPC response JSON
|
||||
|
||||
Returns:
|
||||
list[AURPackage]: list of parsed packages
|
||||
+1
-1
@@ -74,7 +74,7 @@ class Official(Remote):
|
||||
parse RPC response to package list
|
||||
|
||||
Args:
|
||||
response(dict[str, Any]): RPC response json
|
||||
response(dict[str, Any]): RPC response JSON
|
||||
|
||||
Returns:
|
||||
list[AURPackage]: list of parsed packages
|
||||
+1
-1
@@ -32,7 +32,7 @@ class OfficialSyncdb(Official):
|
||||
updates.
|
||||
|
||||
This approach also has limitations, because we don't require superuser rights (neither going to download database
|
||||
separately), the database file might be outdated and must be handled manually (or kind of). This behaviour might be
|
||||
separately), the database file might be outdated and must be handled manually (or kind of). This behavior might be
|
||||
changed in the future.
|
||||
|
||||
Still we leave search function based on the official repositories RPC.
|
||||
+4
-4
@@ -72,17 +72,17 @@ class PackageArchive:
|
||||
if not PackageArchive.is_elf(binary_file):
|
||||
return []
|
||||
|
||||
elf_file = ELFFile(binary_file) # type: ignore[no-untyped-call]
|
||||
elf_file = ELFFile(binary_file)
|
||||
dynamic_section = next(
|
||||
(section for section in elf_file.iter_sections() # type: ignore[no-untyped-call]
|
||||
(section for section in elf_file.iter_sections()
|
||||
if isinstance(section, DynamicSection)),
|
||||
None)
|
||||
if dynamic_section is None:
|
||||
return []
|
||||
|
||||
return [
|
||||
tag.needed
|
||||
for tag in dynamic_section.iter_tags() # type: ignore[no-untyped-call]
|
||||
tag.needed # type: ignore[attr-defined]
|
||||
for tag in dynamic_section.iter_tags()
|
||||
if tag.entry.d_tag == "DT_NEEDED"
|
||||
]
|
||||
|
||||
+6
-2
@@ -416,7 +416,7 @@ class Sources(LazyLogging):
|
||||
else:
|
||||
patch.write(sources_dir / "PKGBUILD")
|
||||
|
||||
def read(self, sources_dir: Path, commit_sha: str, path: Path) -> str:
|
||||
def read(self, sources_dir: Path, commit_sha: str, path: Path) -> str | None:
|
||||
"""
|
||||
read file content from the specified commit
|
||||
|
||||
@@ -426,6 +426,10 @@ class Sources(LazyLogging):
|
||||
path(Path): path to file inside the repository
|
||||
|
||||
Returns:
|
||||
str: file content at specified commit
|
||||
str | None: file content at specified commit if available
|
||||
"""
|
||||
try:
|
||||
return check_output(*self.git(), "show", f"{commit_sha}:{path}", cwd=sources_dir, logger=self.logger)
|
||||
except CalledProcessError:
|
||||
self.logger.exception("failed to read file %s at %s", path, commit_sha)
|
||||
return None
|
||||
+2
-2
@@ -72,7 +72,7 @@ class Configuration(configparser.RawConfigParser):
|
||||
def __init__(self, allow_no_value: bool = False, allow_multi_key: bool = True) -> None:
|
||||
"""
|
||||
Args:
|
||||
allow_no_value(bool, optional): copies :class:`configparser.RawConfigParser` behaviour. In case if it is set
|
||||
allow_no_value(bool, optional): copies :class:`configparser.RawConfigParser` behavior. In case if it is set
|
||||
to ``True``, the keys without values will be allowed (Default value = False)
|
||||
allow_multi_key(bool, optional): if set to ``False``, then the default dictionary class will be used to
|
||||
store keys internally. Otherwise, the special implementation will be used, which supports arrays
|
||||
@@ -80,7 +80,7 @@ class Configuration(configparser.RawConfigParser):
|
||||
"""
|
||||
configparser.RawConfigParser.__init__(
|
||||
self,
|
||||
dict_type=ConfigurationMultiDict if allow_multi_key else dict, # type: ignore[arg-type]
|
||||
dict_type=ConfigurationMultiDict if allow_multi_key else dict,
|
||||
allow_no_value=allow_no_value,
|
||||
strict=False,
|
||||
empty_lines_in_values=not allow_multi_key,
|
||||
+32
@@ -358,6 +358,38 @@ CONFIGURATION_SCHEMA: ConfigurationSchema = {
|
||||
"min": 0,
|
||||
},
|
||||
},
|
||||
"cors_allow_headers": {
|
||||
"type": "list",
|
||||
"coerce": "list",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"empty": False,
|
||||
},
|
||||
},
|
||||
"cors_allow_methods": {
|
||||
"type": "list",
|
||||
"coerce": "list",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"empty": False,
|
||||
},
|
||||
},
|
||||
"cors_allow_origins": {
|
||||
"type": "list",
|
||||
"coerce": "list",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"empty": False,
|
||||
},
|
||||
},
|
||||
"cors_expose_headers": {
|
||||
"type": "list",
|
||||
"coerce": "list",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"empty": False,
|
||||
},
|
||||
},
|
||||
"enable_archive_upload": {
|
||||
"type": "boolean",
|
||||
"coerce": "boolean",
|
||||
+2
-2
@@ -150,6 +150,6 @@ class ShellTemplate(Template):
|
||||
break
|
||||
|
||||
kwargs.update(mapping)
|
||||
substituted = dict(generator(kwargs))
|
||||
kwargs.update(dict(generator(kwargs)))
|
||||
|
||||
return self.safe_substitute(kwargs | substituted)
|
||||
return self.safe_substitute(kwargs)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user