Add "repo" command

Add a "repo" command to create the Debian package archive.
The purpose of the "repo" command is that allows the user
to create, manage, and add new Debian packages locally which
can be used to update ostree branches if the user does
not have access to a network.

The way that the "repo" command works is that, the "repo"
makes system calls to reprepro to manage the archive.
Reprepro was chosen because its simple to use, proven,
and there is plenty of documentation.

In-order for the package feed for it to be effective, one
usually has a web front end so that apt can interact
with the repository. To set one up please read the following
documentaiton:

https://wiki.debian.org/DebianRepository/SetupWithReprepro

This commits also adds an "init" sub-command which will
initialize the configuration of a Debian package repository
created by reprepro.

Testing:
PASSED Installed apt-ostree from git repo.
PASSED Run "sudo apt-ostree repo init \
	--origin starlingx-updates --release bookworm"
PASSED Check for /var/repository directory on target.
PASSED Check "/var/repository/conf/distributions" to see
       the "Origin:" matches up with the CLI.

Story: 2010867
Task: 48556

Change-Id: I9fd56ab78ec6d164b4486b4ef7ad3592dae56ed7
Signed-off-by: Charles Short <charles.short@windriver.com>
This commit is contained in:
Charles Short 2023-08-15 10:43:45 -04:00
parent b25c2f5150
commit 9ef7d8b93a
8 changed files with 182 additions and 1 deletions

View File

@ -11,6 +11,8 @@ import click
from apt_ostree.cmd import State
"""global options"""
def debug_option(f):
def callback(ctxt, param, value):
@ -53,6 +55,9 @@ def edit_option(f):
)(f)
"""compose options"""
def repo_option(f):
"""ostree repo path option"""
def callback(ctxt, param, value):
@ -102,3 +107,53 @@ def compose_options(f):
f = branch_option(f)
f = edit_option(f)
return f
"""Package feed options"""
def feed_option(f):
"""package feed directory"""
def callback(ctxt, param, value):
state = ctxt.ensure_object(State)
state.feed = pathlib.Path(value)
return value
return click.option(
"--feed",
help="Directory to store package repository",
nargs=1,
required=True,
default="/var/repository",
callback=callback
)(f)
def release_option(f):
"""release option"""
def callback(ctxt, param, value):
state = ctxt.ensure_object(State)
state.release = value
return value
return click.option(
"--release",
help="Debian codename release",
nargs=1,
required=True,
callback=callback,
type=click.Choice(["bookworm", "bullseye"]),
)(f)
def origin_option(f):
"""Origin option"""
def callback(ctxt, param, value):
state = ctxt.ensure_object(State)
state.origin = value
return value
return click.option(
"--origin",
help="Debian package origin (e.g. updates)",
nargs=1,
required=True,
callback=callback
)(f)

View File

@ -0,0 +1,19 @@
"""
Copyright (c) 2023 Wind River Systems, Inc.
SPDX-License-Identifier: Apache-2.0
"""
import click
from apt_ostree.cmd.repo.init import init
@click.group(help="Commands to create/manage Debian package repository")
@click.pass_context
def repo(ctxt):
pass
repo.add_command(init)

View File

@ -0,0 +1,34 @@
"""
Copyright (c) 2023 Wind River Systems, Inc.
SPDX-License-Identifier: Apache-2.0
"""
import shutil
import sys
import click
from apt_ostree.cmd.options import feed_option
from apt_ostree.cmd.options import origin_option
from apt_ostree.cmd.options import release_option
from apt_ostree.cmd import pass_state_context
from apt_ostree.log import complete_step
from apt_ostree.repo import Repo
@click.command(help="Create a Debian package repsoitory.")
@pass_state_context
@feed_option
@release_option
@origin_option
def init(state, feed, release, origin):
if shutil.which("reprepro") is None:
click.secho("reprepro was not found in your $PATH")
sys.exit(0)
with complete_step(
f"Creating Debian package repository in {state.feed}."):
Repo(state).init()

View File

@ -13,6 +13,7 @@ from apt_ostree.cmd.compose import compose
from apt_ostree.cmd.options import debug_option
from apt_ostree.cmd.options import workspace_option
from apt_ostree.cmd import pass_state_context
from apt_ostree.cmd.repo import repo
from apt_ostree.cmd.version import version
from apt_ostree.log import setup_log
@ -39,4 +40,5 @@ def main():
cli.add_command(compose)
cli.add_command(repo)
cli.add_command(version)

52
apt_ostree/repo.py Normal file
View File

@ -0,0 +1,52 @@
"""
Copyright (c) 2023 Wind River Systems, Inc.
SPDX-License-Identifier: Apache-2.0
"""
import sys
import textwrap
from apt_ostree.log import log_step
class Repo:
def __init__(self, state):
self.state = state
self.repo = self.state.feed
self.label = "StarlingX project udpates."
self.arch = "amd64"
self.description = "Apt repository for StarlingX updates."
def init(self):
"""Create a debian archive from scratch."""
log_step("Creating Debian package archive.")
self.repo = self.repo.joinpath("conf")
if not self.repo.exists():
log_step("Creating package feed directory")
self.repo.mkdir(parents=True, exist_ok=True)
config = self.repo.joinpath("distributions")
if config.exists():
log_step("Found existing configuration")
sys.exit(1)
else:
log_step("Creating reprepro configuration")
config.write_text(
textwrap.dedent(f"""\
Origin: {self.state.origin}
Label: {self.label}
Codename: {self.state.release}
Architectures: amd64
Components: {self.state.origin}
Description: {self.description}
""")
)
options = self.repo.joinpath("options")
if not options.exists():
options.write_text(
textwrap.dedent(f"""\
basedir {self.repo}
""")
)

View File

View File

@ -0,0 +1,18 @@
"""
Copyright (c) 2023 Wind River Systems, Inc.
SPDX-License-Identifier: Apache-2.0
"""
from apt_ostree.cmd.shell import cli
from apt_ostree.tests import base
from click.testing import CliRunner
class TestRepoCLI(base.TestCase):
def test_repo_help(self):
runner = CliRunner()
result = runner.invoke(cli, ["repo", "--help"])
assert result.exit_code == 0

View File

@ -14,7 +14,8 @@ RUN apt-get update && \
python3-click \
python3-pip \
python3-pbr \
python3-rich
python3-rich \
reprepro
ADD assets/pip.conf /etc/pip.conf