FDS/OS 1.0 fixes
This commit is contained in:
@@ -61,7 +61,8 @@ class CleanupTests(unittest.TestCase):
|
||||
self.file('out/manifests/acceptance.json'),
|
||||
self.file('out/.gitkeep'),
|
||||
self.file('.host/xbps/tool'),
|
||||
self.file('vendor/void-packages/hostdir/download'),
|
||||
self.file('.host/void-packages/hostdir/download'),
|
||||
self.file('vendor/void-packages/xbps-src'),
|
||||
]
|
||||
(self.root / 'out/rootfs-cli.tar').symlink_to('rootfs-build.NEW123/rootfs.tar')
|
||||
preview = self.run_cli('--dry-run')
|
||||
|
||||
@@ -30,31 +30,65 @@ expect_failure 'unsupported bootstrap option' tools/bootstrap-host --unknown
|
||||
|
||||
# Use a disposable minimal checkout to exercise pin and dirty-tree detection.
|
||||
mkdir -p "$scratch/repo/tools" "$scratch/repo/vendor/void-packages"
|
||||
cp tools/lib.sh tools/prepare-void "$scratch/repo/tools/"
|
||||
cp tools/lib.sh tools/prepare-void tools/prepare-void-workspace "$scratch/repo/tools/"
|
||||
fake="$scratch/repo/vendor/void-packages"
|
||||
git -C "$fake" init -q
|
||||
printf '# fixture\n' >"$fake/xbps-src"
|
||||
mkdir -p "$fake/srcpkgs/fds-upstream" "$fake/etc" "$scratch/repo/config" "$scratch/repo/packages/fds-fixture"
|
||||
printf '# upstream\n' >"$fake/srcpkgs/fds-upstream/template"
|
||||
printf 'etc/conf\n' >"$fake/.gitignore"
|
||||
printf 'etc/conf\nhostdir/\nmasterdir-*/\n' >"$fake/.gitignore"
|
||||
cp config/xbps-src.conf "$scratch/repo/config/"
|
||||
printf '# inert overlay fixture\n' >"$scratch/repo/packages/fds-fixture/template"
|
||||
git -C "$fake" add .
|
||||
git -C "$fake" -c user.name=FDS -c user.email=test@example.invalid commit -qm fixture
|
||||
git -C "$fake" rev-parse HEAD >"$scratch/repo/VOID_PACKAGES_COMMIT"
|
||||
bash -c 'source "$1"; check_void_pin' _ "$scratch/repo/tools/lib.sh"
|
||||
bash -c 'source "$1"; check_void_source' _ "$scratch/repo/tools/lib.sh"
|
||||
printf 'PASS: accepts exact clean Void commit\n'
|
||||
# Existing generated files and caches move without copying their payloads.
|
||||
cp -a "$scratch/repo/packages/fds-fixture" "$fake/srcpkgs/"
|
||||
cp config/xbps-src.conf "$fake/etc/conf"
|
||||
mkdir -p "$fake/hostdir" "$fake/masterdir-x86_64"
|
||||
printf 'cached input\n' >"$fake/hostdir/cache"
|
||||
cache_inode=$(stat -c %i "$fake/hostdir/cache")
|
||||
"$scratch/repo/tools/prepare-void"
|
||||
cmp "$scratch/repo/packages/fds-fixture/template" "$fake/srcpkgs/fds-fixture/template"
|
||||
build="$scratch/repo/.host/void-packages"
|
||||
[[ -z $(git -C "$fake" status --porcelain --untracked-files=all) ]] || die 'Migration dirtied upstream'
|
||||
[[ ! -e $fake/hostdir && ! -e $fake/etc/conf && ! -e $fake/srcpkgs/fds-fixture ]] || die 'Migration left build state in upstream'
|
||||
[[ $(stat -c %i "$build/hostdir/cache") == "$cache_inode" ]] || die 'Migration copied cache data'
|
||||
[[ ! -f $build/.git/objects/info/alternates ]] || die 'Build checkout depends on upstream object storage'
|
||||
bash -c 'source "$1"; check_void_pin' _ "$scratch/repo/tools/lib.sh"
|
||||
cmp "$scratch/repo/packages/fds-fixture/template" "$build/srcpkgs/fds-fixture/template"
|
||||
"$scratch/repo/tools/prepare-void"
|
||||
printf 'PASS: overlay copied and repeat preparation is idempotent\n'
|
||||
printf 'PASS: overlays and caches moved outside clean upstream; preparation is idempotent\n'
|
||||
printf '# changed\n' >>"$scratch/repo/packages/fds-fixture/template"
|
||||
expect_failure 'stale generated overlay' "$scratch/repo/tools/prepare-void"
|
||||
cp "$fake/srcpkgs/fds-fixture/template" "$scratch/repo/packages/fds-fixture/template"
|
||||
cp "$build/srcpkgs/fds-fixture/template" "$scratch/repo/packages/fds-fixture/template"
|
||||
# Preserve unexpected upstream additions and independently edited legacy copies.
|
||||
printf 'personal source\n' >"$fake/personal-file"
|
||||
expect_failure 'unknown upstream addition' "$scratch/repo/tools/prepare-void"
|
||||
[[ -f $fake/personal-file ]] || die 'Unknown file was removed'
|
||||
rm "$fake/personal-file"
|
||||
cp -a "$scratch/repo/packages/fds-fixture" "$fake/srcpkgs/"
|
||||
printf '# independent edit\n' >>"$fake/srcpkgs/fds-fixture/template"
|
||||
expect_failure 'edited legacy overlay' "$scratch/repo/tools/prepare-void"
|
||||
grep -q 'independent edit' "$fake/srcpkgs/fds-fixture/template"
|
||||
rm -r "$fake/srcpkgs/fds-fixture"
|
||||
# An explicit upstream pin update advances the build checkout and keeps caches.
|
||||
printf 'new upstream revision\n' >"$fake/README"
|
||||
git -C "$fake" add README
|
||||
git -C "$fake" -c user.name=FDS -c user.email=test@example.invalid commit -qm update
|
||||
git -C "$fake" rev-parse HEAD >"$scratch/repo/VOID_PACKAGES_COMMIT"
|
||||
# Production initializes upstream with depth 1; updating that boundary must work.
|
||||
git -C "$fake" rev-parse HEAD >"$fake/.git/shallow"
|
||||
expect_failure 'outdated build checkout' bash -c 'source "$1"; check_void_pin' _ "$scratch/repo/tools/lib.sh"
|
||||
"$scratch/repo/tools/prepare-void"
|
||||
[[ $(stat -c %i "$build/hostdir/cache") == "$cache_inode" ]] || die 'Pin update replaced cache'
|
||||
[[ $(git -C "$build" rev-parse HEAD) == "$(cat "$scratch/repo/VOID_PACKAGES_COMMIT")" ]] || die 'Build pin not advanced'
|
||||
mkdir -p "$scratch/repo/packages/fds-upstream"
|
||||
printf '# collision\n' >"$scratch/repo/packages/fds-upstream/template"
|
||||
expect_failure 'overlay replacing upstream package' "$scratch/repo/tools/prepare-void"
|
||||
printf '# changed config\n' >>"$fake/etc/conf"
|
||||
rm -r "$scratch/repo/packages/fds-upstream"
|
||||
printf '# changed config\n' >>"$build/etc/conf"
|
||||
expect_failure 'overwriting local xbps configuration' "$scratch/repo/tools/prepare-void"
|
||||
printf '\n# dirty\n' >>"$fake/xbps-src"
|
||||
expect_failure 'modified Void source' bash -c 'source "$1"; check_void_pin' _ "$scratch/repo/tools/lib.sh"
|
||||
|
||||
@@ -131,4 +131,46 @@ command[-1] = linked
|
||||
result = invoke(*command, ok=False)
|
||||
assert 'not a symlink' in result.stderr
|
||||
print(f'PASS: release output preservation and private-key symlink rejection: {work}')
|
||||
|
||||
# Restore small real Git bundles through both workspace layouts. These fixtures
|
||||
# exercise path selection and cache preservation, not an operating-system build.
|
||||
upstream = work / 'upstream'
|
||||
upstream.mkdir()
|
||||
invoke('git', 'init', '-q', upstream)
|
||||
(upstream / 'xbps-src').write_text('# inert source fixture\n')
|
||||
(upstream / '.gitignore').write_text('hostdir/\nmasterdir-*/\netc/conf\netc/xbps.d/custom/\n')
|
||||
invoke('git', '-C', upstream, 'add', '.')
|
||||
invoke('git', '-C', upstream, '-c', 'user.name=FDS', '-c', 'user.email=test@example.invalid', 'commit', '-qm', 'fixture')
|
||||
pin = invoke('git', '-C', upstream, 'rev-parse', 'HEAD').stdout.strip()
|
||||
epoch = int(invoke('git', '-C', upstream, 'show', '-s', '--format=%ct', 'HEAD').stdout)
|
||||
for layout in ('separate', 'legacy'):
|
||||
fixture = work / ('restore-input-' + layout)
|
||||
(fixture / 'project/tools').mkdir(parents=True)
|
||||
(fixture / 'project/config').mkdir()
|
||||
(fixture / 'project/VOID_PACKAGES_COMMIT').write_text(pin + '\n')
|
||||
(fixture / 'project/config/xbps-src.conf').write_text('# fixture\n')
|
||||
if layout == 'separate':
|
||||
shutil.copy2(project / 'tools/prepare-void-workspace', fixture / 'project/tools/prepare-void-workspace')
|
||||
invoke('git', '-C', upstream, 'bundle', 'create', fixture / 'void.bundle', 'HEAD')
|
||||
for name in ('masterdir/etc/xbps.d', 'sources', 'repositories/build',
|
||||
'repositories/cli', 'repositories/development', 'repositories/recovery',
|
||||
'xbps', 'image-tools', 'eeprom', 'cache', 'downloads', 'cargo-vendor', 'rust-toolchain'):
|
||||
(fixture / name).mkdir(parents=True, exist_ok=True)
|
||||
(fixture / 'sources/retained.tar.gz').write_bytes(b'cached source fixture')
|
||||
restore_lock = dict(lock, void_commit=pin, source_epoch=epoch)
|
||||
restore_lock['files'] = frozen.inventory(fixture)
|
||||
restore_lock['source_sha256'] = frozen.source_digest(restore_lock['files'])
|
||||
(fixture / 'lock.json').write_text(json.dumps(restore_lock))
|
||||
destination = work / ('restored-' + layout)
|
||||
invoke(verify, 'restore', fixture, destination)
|
||||
build = destination / ('.host/void-packages' if layout == 'separate' else 'vendor/void-packages')
|
||||
assert (build / 'hostdir/sources/retained.tar.gz').read_bytes() == b'cached source fixture'
|
||||
assert (build / 'masterdir-x86_64/etc/xbps.d/05-fds-frozen-local.conf').read_text() == 'repository=/host/frozen-repository\n'
|
||||
assert invoke('git', '-C', build, 'rev-parse', 'HEAD').stdout.strip() == pin
|
||||
if layout == 'separate':
|
||||
reference = destination / 'vendor/void-packages'
|
||||
assert not (reference / 'hostdir').exists()
|
||||
assert not invoke('git', '-C', reference, 'status', '--porcelain', '--untracked-files=all').stdout.strip()
|
||||
invoke(verify, 'verify', fixture)
|
||||
print('PASS: separate-workspace and legacy frozen restores retain source caches and offline repositories', flush=True)
|
||||
print('NOTE: these are small contract fixtures; actual source-to-image offline reproduction remains a separate gate')
|
||||
|
||||
@@ -39,6 +39,16 @@ def invoke(*command, ok=True):
|
||||
result = subprocess.run([cli, '--session', str(session), *map(str, command)], capture_output=True, text=True, timeout=240)
|
||||
log.write(repr(command) + '\n' + result.stdout + result.stderr)
|
||||
log.flush()
|
||||
if (result.returncode == 0) != ok and command and command[0] == 'guest':
|
||||
# Preserve live service evidence before the finally block stops the VM.
|
||||
# Do not retry or relax the failed command's acceptance condition.
|
||||
diagnostics = subprocess.run([
|
||||
cli, '--session', str(session), 'guest', '--', 'sh', '-c',
|
||||
'cat /run/log/cartridged/current; s6-svstat /run/service/cartridged; '
|
||||
'dmesg | tail -40',
|
||||
], capture_output=True, text=True, timeout=60)
|
||||
log.write('Failure diagnostics:\n' + diagnostics.stdout + diagnostics.stderr)
|
||||
log.flush()
|
||||
assert (result.returncode == 0) == ok, (command, result.returncode, result.stdout, result.stderr)
|
||||
return result.stdout
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ from image_formats import gpt, LINUX_FILESYSTEM
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--cli', type=Path, required=True)
|
||||
parser.add_argument('--image-tool-runner', type=Path)
|
||||
parser.add_argument('--void-packages', type=Path, default=project / 'vendor/void-packages')
|
||||
parser.add_argument('--void-packages', type=Path, default=project / '.host/void-packages')
|
||||
parser.add_argument('--xbps-tool-runner', type=Path)
|
||||
parser.add_argument('--xbps-bin', type=Path)
|
||||
args = parser.parse_args()
|
||||
@@ -43,6 +43,15 @@ def digest(path):
|
||||
return hashlib.file_digest(stream, 'sha256').hexdigest()
|
||||
|
||||
|
||||
# A user-provided submodule path must fail before copying any package sources.
|
||||
upstream = project / 'vendor/void-packages'
|
||||
assert not subprocess.check_output(['git', '-C', str(upstream), 'status', '--porcelain', '--untracked-files=all'])
|
||||
rejected = subprocess.run([cli[0], '--void-packages', str(upstream), 'software', 'build',
|
||||
str(project / 'examples/software/hello/software.toml'), str(work / 'forbidden-build')],
|
||||
capture_output=True, text=True)
|
||||
assert rejected.returncode != 0 and 'outside Git submodules' in rejected.stderr, rejected.stderr
|
||||
assert not (work / 'forbidden-build').exists()
|
||||
|
||||
for name in ['hello', 'report']:
|
||||
recipe = project / f'examples/software/{name}/software.toml'
|
||||
if name == 'report':
|
||||
@@ -149,7 +158,8 @@ for case in ['program-corrupt', 'catalogue-mapping']:
|
||||
malformed = work / (case + '.img')
|
||||
gpt(malformed, [(part['name'], LINUX_FILESYSTEM, filesystem) for part, filesystem in zip(parts, selected)])
|
||||
invoke(['inspect', malformed], False)
|
||||
record = dict(status='passed', work=str(work), cli_sha256=digest(args.cli.resolve()), compiled_aarch64_software=True,
|
||||
assert not subprocess.check_output(['git', '-C', str(upstream), 'status', '--porcelain', '--untracked-files=all'])
|
||||
record = dict(source_checkout_clean=True, submodule_build_rejected=True, status='passed', work=str(work), cli_sha256=digest(args.cli.resolve()), compiled_aarch64_software=True,
|
||||
void_source_packages=True, installed_runtime_dependencies=True, wrong_elf_and_escaping_symlink_rejected=True, shared_and_separate_payload_partitions=True,
|
||||
repeat_image_identical=True, direct_installed_erofs_programs=True,
|
||||
exact_and_larger_target_readback=True, wrong_confirmation_unchanged=True,
|
||||
|
||||
Reference in New Issue
Block a user