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

KSP2 netkan validation #91

Merged
merged 1 commit into from
Mar 14, 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
6 changes: 6 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ inputs:
required: false
default: info

game:
description: |-
Short name of the game to be used for inflation, either KSP or KSP2
required: false
default: KSP

source:
description: |-
What to test:
Expand Down
3 changes: 2 additions & 1 deletion ckan_meta_tester/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ def test_metadata() -> None:

github_token = environ.get('GITHUB_TOKEN')

ex = CkanMetaTester(environ.get('GITHUB_ACTOR') == 'netkan-bot')
ex = CkanMetaTester(environ.get('GITHUB_ACTOR') == 'netkan-bot',
environ.get('INPUT_GAME', 'KSP'))
sys.exit(ExitStatus.success
if ex.test_metadata(environ.get('INPUT_SOURCE', 'netkans'),
get_pr_body(github_token, environ.get('INPUT_PULL_REQUEST_URL')),
Expand Down
16 changes: 3 additions & 13 deletions ckan_meta_tester/ckan_install.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,22 @@
import re
import requests
import logging
from collections import OrderedDict
from difflib import unified_diff
from typing import List, Optional

from netkan.metadata import Ckan
from netkan.repos import CkanMetaRepo

from .game import Game
from .game_version import GameVersion


# Should be a class constant, but then we can't access it from KNOWN_VERSIONS's lambda
BUILD_PATTERN=re.compile('\.[0-9]+$')

class CkanInstall(Ckan):
"""Metadata file representation with extensions for installation"""

BUILDS_URL = 'https://raw.githubusercontent.com/KSP-CKAN/CKAN/master/Core/builds.json'
KNOWN_VERSIONS = [GameVersion(v) for v in OrderedDict.fromkeys(map(
lambda v: BUILD_PATTERN.sub('', v),
requests.get(BUILDS_URL).json().get('builds').values()))]

def compat_versions(self) -> List[GameVersion]:
def compat_versions(self, game: Game) -> List[GameVersion]:
minv = self.lowest_compat()
maxv = self.highest_compat()
logging.debug('Finding versions from %s to %s', minv, maxv)
return [v for v in self.KNOWN_VERSIONS
return [v for v in game.versions
if v.compatible(minv, maxv)]

def lowest_compat(self) -> GameVersion:
Expand Down
14 changes: 9 additions & 5 deletions ckan_meta_tester/ckan_meta_tester.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from netkan.repos import CkanMetaRepo

from .ckan_install import CkanInstall
from .game import Game
from .game_version import GameVersion
from .dummy_game_instance import DummyGameInstance
from .log_group import LogGroup
Expand Down Expand Up @@ -48,10 +49,11 @@ class CkanMetaTester:
'EVENT_BEFORE'
]

def __init__(self, i_am_the_bot: bool) -> None:
def __init__(self, i_am_the_bot: bool, game_id: str) -> None:
self.source_to_ckans: OD[Path, List[Path]] = OrderedDict()
self.failed = False
self.i_am_the_bot = i_am_the_bot
self.game = Game.from_id(game_id)
makedirs(self.INFLATED_PATH, exist_ok=True)
makedirs(self.REPO_PATH, exist_ok=True)

Expand All @@ -69,7 +71,7 @@ def test_metadata(self, source: str = 'netkans', pr_body: str = '', github_token
Repo('.').git.execute(['git', 'config', '--global', '--add', 'safe.directory', '/github/workspace'])
logging.debug('Starting metadata test')
self.debug_action()
logging.debug('Builds: %s', [str(v) for v in CkanInstall.KNOWN_VERSIONS])
logging.debug('Builds: %s', [str(v) for v in self.game.versions])

# Escape hatch in case author replaces a download after a previous success
# (which will save it to the persistent cache)
Expand Down Expand Up @@ -141,6 +143,7 @@ def inflate_file(self, file: Path, overwrite_cache: bool, github_token: Optional
if not self.run_for_file(
file,
['mono', self.NETKAN_PATH,
'--game', self.game.short_name,
*(['--github-token', github_token] if github_token is not None else []),
'--cachedir', self.CACHE_PATH,
*(['--highest-version', str(high_ver)] if high_ver else []),
Expand All @@ -164,6 +167,7 @@ def validate_file(self, file: Path, overwrite_cache: bool, github_token: Optiona
if not self.run_for_file(
file,
['mono', self.NETKAN_PATH,
'--game', self.game.short_name,
*(['--github-token', github_token] if github_token is not None else []),
'--cachedir', self.CACHE_PATH,
'--net-useragent', self.USER_AGENT,
Expand All @@ -184,14 +188,14 @@ def install_ckan(self, file: Path, orig_file: Path, pr_body: Optional[str], meta
print(diff, end='', flush=True)
with LogGroup(f'Installing {ckan.name} {ckan.version}'):
versions = [*self.pr_body_versions(pr_body),
*ckan.compat_versions()]
*ckan.compat_versions(self.game)]
if len(versions) < 1:
print(f'::error file={orig_file}::{file} is not compatible with any game versions!', flush=True)
return False

with DummyGameInstance(
Path('/game-instance'), self.CKAN_PATH, self.TINY_REPO,
versions[-1], versions[:-1], self.CACHE_PATH):
versions[-1], versions[:-1], self.CACHE_PATH, self.game):

return self.run_for_file(
orig_file,
Expand All @@ -210,7 +214,7 @@ def install_identifiers(self, identifiers: List[str], pr_body: Optional[str]) ->

with DummyGameInstance(
Path('/game-instance'), self.CKAN_PATH, self.TINY_REPO,
versions[-1], versions[:-1], self.CACHE_PATH):
versions[-1], versions[:-1], self.CACHE_PATH, self.game):

return self.run_for_file(
None,
Expand Down
5 changes: 4 additions & 1 deletion ckan_meta_tester/dummy_game_instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from types import TracebackType
from typing import Type, List

from .game import Game
from .game_version import GameVersion


Expand All @@ -14,14 +15,15 @@ class DummyGameInstance:
MAKING_HISTORY_VERSION=GameVersion('1.4.1')
BREAKING_GROUND_VERSION=GameVersion('1.7.1')

def __init__(self, where: Path, ckan_exe: Path, addl_repo: Path, main_ver: GameVersion, other_versions: List[GameVersion], cache_path: Path) -> None:
def __init__(self, where: Path, ckan_exe: Path, addl_repo: Path, main_ver: GameVersion, other_versions: List[GameVersion], cache_path: Path, game: Game) -> None:
self.where = where
self.registry_path = self.where.joinpath('CKAN').joinpath('registry.json')
self.ckan_exe = ckan_exe
self.addl_repo = addl_repo
self.main_ver = main_ver
self.other_versions = other_versions
self.cache_path = cache_path
self.game = game
# Hide ckan.exe output unless debugging is enabled
self.capture = not logging.getLogger().isEnabledFor(logging.DEBUG)

Expand All @@ -31,6 +33,7 @@ def __enter__(self) -> 'DummyGameInstance':
logging.debug('Populating fake instance contents')
run(['mono', self.ckan_exe,
'instance', 'fake',
'--game', self.game.short_name,
'--set-default', '--headless',
'dummy', self.where, str(self.main_ver),
*self._available_dlcs(self.main_ver)],
Expand Down
55 changes: 55 additions & 0 deletions ckan_meta_tester/game.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import re
import requests
from collections import OrderedDict
from typing import List, Dict, Optional, cast

from .game_version import GameVersion

class Game:
BUILDS_URL = ''

def __init__(self) -> None:
self.versions = self._versions_from_json(
requests.get(self.BUILDS_URL).json())

@property
def short_name(self) -> str:
raise NotImplementedError

def _versions_from_json(self, json: object) -> List[GameVersion]:
raise NotImplementedError

@staticmethod
def from_id(game_id: str = 'KSP') -> 'Game':
if game_id == 'KSP':
return Ksp1()
if game_id == 'KSP2':
return Ksp2()
raise ValueError('game_id must be either KSP or KSP2')


class Ksp1(Game):
BUILDS_URL = 'https://raw.githubusercontent.com/KSP-CKAN/CKAN-meta/master/builds.json'
BUILD_PATTERN=re.compile(r'\.[0-9]+$')

@property
def short_name(self) -> str:
return 'KSP'

def _versions_from_json(self, json: object) -> List[GameVersion]:

return [GameVersion(v) for v in OrderedDict.fromkeys(map(
lambda v: self.BUILD_PATTERN.sub('', v),
cast(Dict[str, Dict[str, str]], json).get('builds', {}).values()))]


class Ksp2(Game):
BUILDS_URL = 'https://raw.githubusercontent.com/KSP-CKAN/KSP2-CKAN-meta/master/builds.json'

@property
def short_name(self) -> str:
return 'KSP2'

def _versions_from_json(self, json: object) -> List[GameVersion]:
# The KSP2 builds.json doesn't have build ids
return [GameVersion(v) for v in cast(List[str], json)]
4 changes: 2 additions & 2 deletions tests/ckan_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from ckan_meta_tester.ckan_install import CkanInstall
from ckan_meta_tester.game_version import GameVersion

from ckan_meta_tester.game import Ksp1

class TestCkanInstall(TestCase):

Expand All @@ -24,7 +24,7 @@ def test_ckan_install(self) -> None:
# Act / Assert
self.assertEqual(cki.lowest_compat(), GameVersion('1.8'))
self.assertEqual(cki.highest_compat(), GameVersion('1.10'))
self.assertEqual(cki.compat_versions(), [
self.assertEqual(cki.compat_versions(Ksp1()), [
GameVersion('1.8.0'), GameVersion('1.8.1'),
GameVersion('1.9.0'), GameVersion('1.9.1'),
GameVersion('1.10.0'), GameVersion('1.10.1'),
Expand Down
4 changes: 2 additions & 2 deletions tests/ckan_meta_tester.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
class TestCkanMetaTester(unittest.TestCase):

def test_true(self) -> None:
tester = CkanMetaTester(False)
tester = CkanMetaTester(False, 'KSP')
self.assertTrue(tester.test_metadata())

def test_pr_body_tests(self) -> None:
tester = CkanMetaTester(False)
tester = CkanMetaTester(False, 'KSP')
result = tester.pr_body_tests("""
## Description
Basic test case
Expand Down
5 changes: 4 additions & 1 deletion tests/dummy_game_instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from unittest.mock import Mock, patch, call
from tempfile import TemporaryDirectory, TemporaryFile

from ckan_meta_tester.game import Game
from ckan_meta_tester.game_version import GameVersion
from ckan_meta_tester.dummy_game_instance import DummyGameInstance

Expand Down Expand Up @@ -34,7 +35,8 @@ def test_dummy_game_instance_calls(self,
Path('/repo/metadata.tar.gz'),
GameVersion('1.8.1'),
[GameVersion('1.8.0')],
Path('/cache')):
Path('/cache'),
Game.from_id('KSP')):

pass

Expand All @@ -52,6 +54,7 @@ def test_dummy_game_instance_calls(self,
])
self.assertEqual(mocked_run.mock_calls, [
call(['mono', PosixPath('/ckan.exe'), 'instance', 'fake',
'--game', 'KSP',
'--set-default', '--headless', 'dummy',
PosixPath('/game-instance'), '1.8.1',
'--MakingHistory', '1.1.0', '--BreakingGround', '1.0.0'],
Expand Down