Skip to content
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
33 changes: 20 additions & 13 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,21 @@ jobs:
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
steps:
- name: Check out repo
- name: Check out repo
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
fetch-tags: true
fetch-tags: true

- name: Install test and packaging dependencies
run: |
sudo apt-get update
sudo apt-get install -y make shellcheck unzip zip

- name: Run tests and ShellCheck
run: |
make test
shellcheck chdtool.sh scripts/*.sh tests/*.sh tests/bin/*

# Generate release notes for this tag (used as Release body)
- name: Generate release notes with git-cliff
Expand All @@ -33,23 +43,20 @@ jobs:
OUTPUT: CHANGELOG_RELEASE.md
GITHUB_REPO: ${{ github.repository }}

- name: Prepare artifacts
run: |
set -euo pipefail
chmod +x chdtool.sh || true
cp chdtool.sh chdtool
mkdir -p dist
tar -czf "dist/chdtool-${GITHUB_REF_NAME}.tar.gz" chdtool README.md LICENSE CHANGELOG.md || true
zip -j "dist/chdtool-${GITHUB_REF_NAME}.zip" chdtool README.md LICENSE CHANGELOG.md || true
- name: Prepare artifacts
run: |
set -euo pipefail
release_version="${GITHUB_REF_NAME#v}"
SOURCE_DATE_EPOCH="$(git log -1 --format=%ct)" \
bash scripts/package-release.sh "$release_version"

- name: Create GitHub Release and upload assets
uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2
with:
tag_name: ${{ github.ref_name }}
body_path: ${{ steps.cliff_notes.outputs.changelog }} # file generated above
files: |
dist/*
chdtool.sh
files: |
dist/*
draft: false
prerelease: ${{ contains(github.ref_name, '-') }}
env:
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/shellcheck.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ on:
- "chdtool.sh"
- "tests/**/*.sh"
- "tests/bin/*"
- "scripts/**/*.sh"
- ".github/workflows/shellcheck.yml"
pull_request:
paths:
- "chdtool.sh"
- "tests/**/*.sh"
- "tests/bin/*"
- "scripts/**/*.sh"
- ".github/workflows/shellcheck.yml"

concurrency:
Expand All @@ -32,4 +34,4 @@ jobs:
- name: Install ShellCheck
run: sudo apt-get update && sudo apt-get install -y shellcheck
- name: Run ShellCheck
run: shellcheck chdtool.sh tests/*.sh tests/bin/*
run: shellcheck chdtool.sh scripts/*.sh tests/*.sh tests/bin/*
2 changes: 2 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ on:
- "chdtool.sh"
- "Makefile"
- "tests/**"
- "scripts/**"
- ".github/workflows/tests.yml"
pull_request:
paths:
- "chdtool.sh"
- "Makefile"
- "tests/**"
- "scripts/**"
- ".github/workflows/tests.yml"

concurrency:
Expand Down
18 changes: 14 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ SHELL := /usr/bin/env bash
# Path to your script; override with: make test SCRIPT=./path/to/script.sh
SCRIPT ?= ./chdtool.sh

.PHONY: test test-m3u test-m3u-single test-partial-resume test-transactional-source-deletion test-temporary-chd-cleanup test-exit-status test-logging test-security-validation test-resource-handling test-regression-gaps test-setup clean changelog changelog-tag
.PHONY: test test-m3u test-m3u-single test-partial-resume test-transactional-source-deletion test-temporary-chd-cleanup test-exit-status test-logging test-security-validation test-resource-handling test-regression-gaps test-cli test-package test-setup clean changelog changelog-tag package

test: test-setup test-m3u test-m3u-single test-partial-resume test-transactional-source-deletion test-temporary-chd-cleanup test-exit-status test-logging test-security-validation test-resource-handling test-regression-gaps
test: test-setup test-m3u test-m3u-single test-partial-resume test-transactional-source-deletion test-temporary-chd-cleanup test-exit-status test-logging test-security-validation test-resource-handling test-regression-gaps test-cli test-package

test-setup:
chmod +x tests/bin/chdman
Expand All @@ -18,6 +18,7 @@ test-setup:
chmod +x tests/test_security_validation.sh
chmod +x tests/test_resource_handling.sh
chmod +x tests/test_regression_gaps.sh
chmod +x tests/test_cli.sh tests/test_package.sh scripts/package-release.sh
@if [ -f tests/test_logging.sh ]; then chmod +x tests/test_logging.sh; fi

test-m3u:
Expand Down Expand Up @@ -54,11 +55,20 @@ test-resource-handling:
test-regression-gaps:
SCRIPT=$(SCRIPT) bash tests/test_regression_gaps.sh

test-cli:
SCRIPT=$(SCRIPT) bash tests/test_cli.sh

test-package:
bash tests/test_package.sh

clean:
@echo "Nothing to clean; tests use mktemp dirs."

changelog:
git cliff --config .cliff.toml -o CHANGELOG.md
git cliff --config cliff.toml -o CHANGELOG.md

changelog-tag:
git cliff --config .cliff.toml --tag $${TAG} --strip header > CHANGELOG_RELEASE.md
git cliff --config cliff.toml --tag $${TAG} --strip header > CHANGELOG_RELEASE.md

package:
bash scripts/package-release.sh "$${VERSION:-$$(sed -n 's/^CHDTOOL_VERSION="\([^"]*\)"/\1/p' chdtool.sh)}"
57 changes: 46 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# 🎮 chdtool

[![License](https://img.shields.io/github/license/lloydsmart/chdtool)](https://github.com/lloydsmart/chdtool/blob/main/LICENSE)
[![License](https://img.shields.io/github/license/lloydsmart/chdtool)](https://github.com/lloydsmart/chdtool/blob/main/LICENSE.md)
[![Release](https://img.shields.io/github/v/release/lloydsmart/chdtool)](https://github.com/lloydsmart/chdtool/releases)
[![ShellCheck](https://img.shields.io/github/actions/workflow/status/lloydsmart/chdtool/shellcheck.yml?branch=main&label=shellcheck)](https://github.com/lloydsmart/chdtool/actions/workflows/shellcheck.yml)

Expand Down Expand Up @@ -31,7 +31,7 @@ Convert everything in a directory:
- 📉 Space savings reporting
- 🧾 Automatic M3U generation for multi-disc sets
- 🧹 Safe temp directory handling with cleanup traps
- 🧪 Dry-run mode (no changes made)
- 🧪 Dry-run mode (no source or conversion changes)
- 🪵 Structured logging system:
- Console / file / syslog / journald
- Configurable verbosity
Expand All @@ -51,9 +51,18 @@ Convert everything in a directory:
| ------ | ----------- |
| `-k`, `--keep-originals` | Do not delete source files after conversion |
| `-r`, `--recursive` | Process subdirectories |
| `-n`, `--dry-run` | Show what would happen without making changes |
| `-F`, `--file-tee` | Force logging to file |
| `-N`, `--no-file-tee` | Disable logging to file |
| `-n`, `--dry-run` | Show what would happen without making changes |
| `-a`, `--allow-unverified-cue-audio` | Permit lossy or unverified CUE audio tracks (not preservation-safe) |
| `-F`, `--file-tee` | Force logging to file |
| `-N`, `--no-file-tee` | Disable logging to file |
| `-h`, `--help` | Show usage information |
| `-V`, `--version` | Show the packaged version |

Use `--` to end option parsing when an input directory starts with `-`:

```bash
./chdtool.sh -- -roms
```

---

Expand Down Expand Up @@ -133,15 +142,21 @@ Configurable via environment variables:
LOG_DEST=auto|console|file|syslog|journald
LOG_LEVEL_THRESHOLD=DEBUG|INFO|WARN|ERROR
LOG_TEE_CONSOLE=auto|1|0
LOG_TEE_FILE=1|0
LOG_TAG=chdtool
LOG_TEE_FILE=1|0
LOG_TAG=chdtool
LOGFILE=/custom/path/chdtool.log
```

Default log file:

```text
logs/chd_conversion_<timestamp>.log
```
```

`--file-tee` and `--no-file-tee` control the optional file mirror for console,
syslog, and journald backends. When `LOG_DEST=file`, the file is the primary
destination and is therefore still written. A caller-supplied `LOGFILE` path is
used verbatim.

---

Expand Down Expand Up @@ -174,8 +189,9 @@ The following tools must be installed:
- `unzip`
- `unrar`
- `7z`
- `stat`
- `awk`
- `stat`
- `awk`
- `stdbuf`

Optional (enhancements):

Expand All @@ -194,9 +210,14 @@ Use `--dry-run` to preview actions:
./chdtool.sh -n /roms
```

- No files are created, moved, or deleted
- No source files, CHDs, playlists, or temporary conversion workspaces are
created, moved, or deleted
- All intended operations are logged

Logging remains active during a dry run and may create the configured log file.
Use `--no-file-tee` with the console, syslog, or journald backend to prevent a
file mirror.

### Resource selection

By default, CHD Tool lets `chdman` choose its format-appropriate hunk size. It
Expand All @@ -222,6 +243,20 @@ result at the available CPU count with a minimum of one.
- Archive members are rejected before conversion when paths escape the extraction
directory, links are present, or selected entries collide after name sanitisation
- Temporary files are cleaned automatically, even on interruption
- `--allow-unverified-cue-audio` relaxes preservation checks for lossy or
otherwise unverified CUE audio tracks; use it only when accepting that risk

---

## 📦 Release assets

Each release publishes `.tar.gz` and `.zip` archives containing `chdtool`,
`README.md`, `LICENSE.md`, and `CHANGELOG.md`, plus a standalone `chdtool`
script. Verify downloads with the published checksum file:

```bash
sha256sum --check SHA256SUMS
```

---

Expand Down
50 changes: 43 additions & 7 deletions chdtool.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,25 @@ script_start_time=$(date +%s)
shopt -s nullglob
shopt -s extglob

USAGE="Usage: $0 [--keep-originals|-k] [--recursive|-r] [--dry-run|-n] [--allow-unverified-cue-audio|-a] [--file-tee|-F|--no-file-tee|-N] <input directory>"
CHDTOOL_VERSION="0.2.4"
PROGRAM_NAME="$(basename -- "$0")"
USAGE="Usage: $PROGRAM_NAME [options] [--] <input directory>"

print_usage() {
cat <<EOF
$USAGE

Options:
-k, --keep-originals Do not delete source files
-r, --recursive Scan subdirectories
-n, --dry-run Preview operations without changing inputs
-a, --allow-unverified-cue-audio Allow lossy/unverified CUE audio tracks
-F, --file-tee Enable file mirroring
-N, --no-file-tee Disable file mirroring
-h, --help Show this help and exit
-V, --version Show the version and exit
EOF
}
KEEP_ORIGINALS=false
RECURSIVE=false
DRY_RUN=false
Expand Down Expand Up @@ -34,6 +52,19 @@ while [[ $# -gt 0 ]]; do
LOG_TEE_FILE=1; shift ;;
--no-file-tee|-N)
LOG_TEE_FILE=0; shift ;;
--help|-h)
print_usage; exit 0 ;;
--version|-V)
printf '%s %s\n' "$PROGRAM_NAME" "$CHDTOOL_VERSION"; exit 0 ;;
--)
shift
if [[ -n "$INPUT_DIR" || $# -ne 1 ]]; then
echo "❌ Expected exactly one input directory after --" >&2
echo "$USAGE" >&2; exit 1
fi
INPUT_DIR="${1%/}"
shift
break ;;
-*)
echo "❌ Unknown option: $1" >&2
echo "$USAGE" >&2; exit 1 ;;
Expand All @@ -51,6 +82,7 @@ done
if [[ -z "$INPUT_DIR" ]]; then
echo "$USAGE" >&2; exit 1
fi
[[ "$INPUT_DIR" == -* ]] && INPUT_DIR="./$INPUT_DIR"
if [[ ! -d "$INPUT_DIR" ]]; then
echo "❌ Input directory does not exist or is not a directory: $INPUT_DIR" >&2
exit 1
Expand All @@ -59,7 +91,7 @@ fi
# Use a disk-based temp directory to avoid filling up RAM
TMP_ROOT="${TMPDIR:-/var/tmp/chdtool}"
TMPDIR="$TMP_ROOT/$RUN_ID"
mkdir -p "$TMPDIR"
[[ "$DRY_RUN" == true ]] || mkdir -p "$TMPDIR"

LOGFILE="${LOGFILE:-logs/chd_conversion_$(date +%Y%m%d_%H%M%S).log}"

Expand Down Expand Up @@ -113,7 +145,7 @@ if [[ "$LOG_BACKEND" == journald || "$LOG_BACKEND" == syslog ]]; then
fi

# ensure the logfile directory exists when we might write to it
if [[ "$LOG_BACKEND" == file || "$LOG_BACKEND" == console ]] || __should_mirror_file; then
if [[ "$LOG_BACKEND" == file ]] || __should_mirror_file; then
mkdir -p -- "$(dirname -- "$LOGFILE")"
fi

Expand Down Expand Up @@ -182,9 +214,13 @@ _emit_log() {
__should_mirror_console && __console_print "$ts" "$lvl" "$msg"
;;
console|*)
while IFS= read -r line; do
printf '[%s] %s: %s\n' "$ts" "$lvl" "$line" | tee -a "$LOGFILE"
done <<< "$msg"
if __should_mirror_file; then
while IFS= read -r line; do
printf '[%s] %s: %s\n' "$ts" "$lvl" "$line" | tee -a "$LOGFILE"
done <<< "$msg"
else
__console_print "$ts" "$lvl" "$msg"
fi
;;
esac

Expand Down Expand Up @@ -503,7 +539,7 @@ validate_extracted_tree() {
done < <(find "$root" -type l -print0)
}

check_temp_storage "$TMPDIR"
[[ "$DRY_RUN" == true ]] || check_temp_storage "$TMPDIR"

# ---------- chdman progress handling ----------
# Config: PROGRESS_STYLE=auto|bar|line|none ; default: auto (TTY -> bar, non-TTY -> none)
Expand Down
Loading