Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ci/package_checks: add check for frozen packages #978

Merged
merged 2 commits into from
Dec 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions common/CI/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
freeze:
start: null
end: null
75 changes: 75 additions & 0 deletions common/CI/package_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import re
import subprocess
from dataclasses import dataclass
from datetime import datetime, timezone
from enum import Enum
from typing import Any, Dict, List, Optional, TextIO, Tuple, Union
from urllib import request
Expand All @@ -14,6 +15,30 @@
import yaml


@dataclass
class FreezeConfig:
start: Optional[datetime]
end: Optional[datetime]

def active(self) -> bool:
now = datetime.now(tz=timezone.utc)

return (self.start is not None and now > self.start and
(self.end is None or now < self.end))


@dataclass
class Config:
freeze: FreezeConfig

@staticmethod
def load(stream: Any) -> 'Config':
return Config(**yaml.safe_load(stream))

def __post_init__(self) -> None:
self.freeze = FreezeConfig(**self.freeze) # type: ignore


class Git:
def __init__(self, path: str):
self.path = path
Expand Down Expand Up @@ -66,6 +91,8 @@ def untracked_files(self) -> List[str]:

class Level(str, Enum):
__str__ = Enum.__str__
DEBUG = 'debug'
NOTICE = 'notice'
ERROR = 'error'
WARNING = 'warning'

Expand Down Expand Up @@ -109,6 +136,7 @@ def _property(self, key: str) -> str:
class PullRequestCheck:
_package_files = ['package.yml']
_two_letter_dirs = ['py']
_config: Optional[Config] = None

def __init__(self, git: Git, files: List[str], commits: List[str], base: Optional[str]):
self.git = git
Expand All @@ -119,6 +147,14 @@ def __init__(self, git: Git, files: List[str], commits: List[str], base: Optiona
def run(self) -> List[Result]:
raise NotImplementedError

@property
def config(self) -> Config:
if self._config is None:
with self._open(os.path.join('common', 'CI', 'config.yaml')) as f:
self._config = Config.load(f)

return self._config

@property
def package_files(self) -> List[str]:
return self._filter_packages(self.files)
Expand Down Expand Up @@ -160,6 +196,14 @@ def package_file(self, package: str, file: str) -> str:
def package_dir(self, package: str) -> str:
return os.path.join('packages', self._package_subdir(package), package)

@staticmethod
def package_for(path: str) -> str:
parts = path.split("/")
if len(parts) != 3 or parts[0] != "packages":
return ""

return parts[2]

def _package_subdir(self, package: str) -> str:
package = package.lower()

Expand Down Expand Up @@ -189,6 +233,36 @@ def _check_commit(self, commit: str) -> List[Result]:
return results


class FrozenPackage(PullRequestCheck):
__packages: Optional[List[str]] = None
__message_normal = ('This package is included in the ISO. '
'Consider validating the functionality in a newly built ISO.')
__message_freeze = ('This package is included in the ISO and is currently frozen. '
'It can only be updated to fix critical bugs, '
'in consultation with multiple Solus staff members.')

def run(self) -> List[Result]:
return [self._make_result(f)
for f in self.package_files
if not self._is_frozen(f)]

def _make_result(self, file: str) -> Result:
if self.config.freeze.active():
return Result(message=self.__message_freeze, file=file, level=Level.WARNING)

return Result(message=self.__message_normal, file=file, level=Level.NOTICE)

def _is_frozen(self, file: str) -> bool:
return self.package_for(file) in self._packages()

def _packages(self) -> List[str]:
if self.__packages is None:
with self._open(os.path.join('common', 'iso_packages.txt')) as file:
self.__packages = [line.strip() for line in file]

return self.__packages


class Homepage(PullRequestCheck):
_error = '`homepage` is not set'
_level = Level.ERROR
Expand Down Expand Up @@ -481,6 +555,7 @@ def _commit_package_yaml(self, ref: str) -> Optional[Dict[str, Any]]:
class Checker:
checks = [
CommitMessage,
FrozenPackage,
Homepage,
PackageBumped,
PackageDependenciesOrder,
Expand Down
Loading