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
1 change: 1 addition & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Checks: >
cppcoreguidelines-no-malloc,
-cppcoreguidelines-owning-memory,
performance-unnecessary-value-param,
readability-identifier-naming,
readability-inconsistent-declaration-parameter-name,
readability-container-size-empty
WarningsAsErrors: "*"
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
run: mdbook build

- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v3
uses: actions/upload-pages-artifact@v5
with:
path: book/

Expand All @@ -50,4 +50,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
uses: actions/deploy-pages@v5
9 changes: 2 additions & 7 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,6 @@ jobs:
- name: Install clang tools
run: make install_clang_tools

- name: Check formatting
run: make lint
- run: make fmt_check

- name: Generate compile_commands.json
run: make build_lint/compile_commands.json

- name: Run clang-tidy
run: make lint_cpp
- run: make lint_cpp
1 change: 1 addition & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on:
- "main"
paths:
- ".github/workflows/**"
- "Makefile"
- ".clang-format"
- ".clang-tidy"
- "src/**"
Expand Down
16 changes: 13 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
# Changelog

## 0.9.10 - 2026-04-27
## 0.9.10 - 2026-06-02

### Added

- Support passing multiple files to the add and remove subcommands
- Support reading file paths from stdin in the add and remove subcommands
- Support adding entire directories with the add subcommand
- Add an update flag to the add subcommand to skip files whose archived size matches the on-disk size

### Fixed

- Extract command now reports an error when the output directory cannot be created
- Path traversal guard in extract uses fully resolved paths, closing a potential bypass
- Crash when reading strong signatures from malformed or truncated archives
- Docker glibc image updated to ubuntu:24.04
- Adding files is now ordered and operating system agnostic

### Updated
### Changed

- Docker glibc image updated to ubuntu:24.04
- The path flag on add now acts as an archive path prefix when a directory is given
- The directory-in-archive and filename-in-archive flags are now restricted to single-file add operations

## 0.9.9 - 2026-04-05

Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ If you are unsure whether a feature fits the project, or whether an existing too
Clone the repository and initialise submodules:

```
git clone https://github.com/TheGrayDot/mpqcli.git
git clone https://github.com/thegraydot/mpqcli.git
cd mpqcli
git submodule update --init --recursive
```
Expand Down
188 changes: 106 additions & 82 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,149 +1,173 @@
SHELL := /bin/bash

CMAKE_BUILD_TYPE := Release
BUILD_MPQCLI := ON
CLANG_VERSION := 18
VERSION := $(shell awk '/project\(MPQCLI VERSION/ {gsub(/\)/, "", $$3); print $$3}' CMakeLists.txt)
README := README.md
PACKAGE_URL := https://github.com/TheGrayDot/mpqcli/pkgs/container/mpqcli

GCC_INSTALL_DIR := $(shell dirname "$(shell gcc -print-libgcc-file-name)")

.PHONY: help \
setup \
build_linux build_windows build_clean build_lint_clean \
docker_musl_build docker_musl_run docker_glibc_build docker_glibc_run \
test_create_venv test_mpqcli test_clean test_lint \
lint_format lint_format_fix lint_cpp lint \
clean \
bump_stormlib bump_cli11 bump_submodules \
fetch_downloads tag_release

## Show this help menu
help:
@awk 'BEGIN {FS = ":"; printf "\nUsage:\n make \033[36m<target>\033[0m\n\nTargets:\n"} \
/^## / {desc = substr($$0, 4); next} \
/^[a-zA-Z0-9_-]+:/ {if (desc) printf " \033[36m%-22s\033[0m %s\n", $$1, desc; desc = ""; next} \
{desc = ""}' $(MAKEFILE_LIST)

## Install clang lint dependencies
install_clang_tools:
sudo apt-get install -y clang-format-$(CLANG_VERSION) clang-tidy-$(CLANG_VERSION)
BUILD_MPQCLI := ON
CLANG_VERSION := 18
VERSION := $(shell awk '/project\(MPQCLI VERSION/ {gsub(/\)/, "", $$3); print $$3}' CMakeLists.txt)
README := README.md
PACKAGE_URL := https://github.com/thegraydot/mpqcli/pkgs/container/mpqcli
GCC_INSTALL_DIR := $(shell dirname "$(shell gcc -print-libgcc-file-name)")
TAG ?= $(shell git describe --tags --abbrev=0 2>/dev/null)

.PHONY: help
help: ## Show this help message
@grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) \
| awk 'BEGIN {FS = ":.*?## "}; {printf " %-22s %s\n", $$1, $$2}'

# BUILD
## Build for Linux using cmake
build_linux:
.PHONY: install_clang_tools
install_clang_tools: ## Install clang lint dependencies
sudo apt-get install -y clang-format-$(CLANG_VERSION) clang-tidy-$(CLANG_VERSION)

.PHONY: configure
configure: ## Configure cmake build (debug, with compile_commands.json)
cmake -B build \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DBUILD_MPQCLI=$(BUILD_MPQCLI) \
-DCMAKE_CXX_COMPILER=clang++-$(CLANG_VERSION) \
-DCMAKE_CXX_FLAGS="--gcc-install-dir=$(GCC_INSTALL_DIR)"

build/compile_commands.json: CMakeLists.txt src/CMakeLists.txt
cmake -B build \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DBUILD_MPQCLI=$(BUILD_MPQCLI) \
-DCMAKE_CXX_COMPILER=clang++-$(CLANG_VERSION) \
-DCMAKE_CXX_FLAGS="--gcc-install-dir=$(GCC_INSTALL_DIR)"

.PHONY: build
build: ## Build via cmake
cmake --build build

.PHONY: build_linux
build_linux: ## Build for Linux using cmake
cmake -B build \
-DCMAKE_BUILD_TYPE=$(CMAKE_BUILD_TYPE) \
-DBUILD_MPQCLI=$(BUILD_MPQCLI)
cmake --build build

## Build for Windows using cmake
build_windows:
.PHONY: build_windows
build_windows: ## Build for Windows using cmake
cmake -B build \
-DCMAKE_BUILD_TYPE=$(CMAKE_BUILD_TYPE) \
-DBUILD_MPQCLI=$(BUILD_MPQCLI)
cmake --build build --config $(CMAKE_BUILD_TYPE)

## Remove cmake build directory
build_clean:
.PHONY: build_clean
build_clean: ## Remove cmake build directory
rm -rf build

## Generate compile_commands.json for clang-tidy
build_lint/compile_commands.json: CMakeLists.txt src/CMakeLists.txt
cmake -B build_lint \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DBUILD_MPQCLI=ON \
-DCMAKE_CXX_COMPILER=clang++-$(CLANG_VERSION) \
-DCMAKE_CXX_FLAGS="--gcc-install-dir=$(GCC_INSTALL_DIR)"

## Remove cmake lint build directory
build_lint_clean:
rm -rf build_lint

# DOCKER
## Build Docker image using musl
docker_musl_build:
.PHONY: docker_musl_build
docker_musl_build: ## Build Docker image using musl
docker build -t mpqcli:$(VERSION) -f Dockerfile.musl .

## Run the musl Docker image
docker_musl_run:
.PHONY: docker_musl_run
docker_musl_run: ## Run the musl Docker image
@docker run -it mpqcli:$(VERSION) version

## Build Docker image using glibc
docker_glibc_build:
.PHONY: docker_glibc_build
docker_glibc_build: ## Build Docker image using glibc
docker build -t mpqcli:$(VERSION) -f Dockerfile.glibc .

## Run the glibc Docker image
docker_glibc_run:
.PHONY: docker_glibc_run
docker_glibc_run: ## Run the glibc Docker image
@docker run -it mpqcli:$(VERSION) version

# TEST
## Create Python venv and install test dependencies
test_create_venv:
.PHONY: test
test: build test_mpqcli ## Run test suite (builds binary first)

.PHONY: test_create_venv
test_create_venv: ## Create Python venv and install test dependencies
python3 -m venv ./.venv
. ./.venv/bin/activate && \
pip3 install -r test/requirements.txt

## Run pytest test suite
test_mpqcli:
.PHONY: test_mpqcli
test_mpqcli: ## Run pytest test suite
. ./.venv/bin/activate && \
python3 -m pytest test -s

## Remove test data directory
test_clean:
.PHONY: test_clean
test_clean: ## Remove test data directory
rm -rf test/data

## Run ruff linter on test directory
test_lint:
.PHONY: test_lint
test_lint: ## Run ruff linter on test directory
. ./.venv/bin/activate && \
ruff check ./test

# LINT
## Check C++ formatting with clang-format
lint_format:
.PHONY: fmt_check
fmt_check: ## Check C++ formatting with clang-format
find src \( -name "*.cpp" -o -name "*.h" \) \
| xargs clang-format-$(CLANG_VERSION) --dry-run --Werror

## Auto-fix C++ formatting with clang-format
lint_format_fix:
.PHONY: fmt
fmt: ## Auto-fix C++ formatting with clang-format
find src \( -name "*.cpp" -o -name "*.h" \) \
| xargs clang-format-$(CLANG_VERSION) -i

## Run clang-tidy static analysis
lint_cpp: build_lint/compile_commands.json
.PHONY: lint_cpp
lint_cpp: build/compile_commands.json ## Run clang-tidy static analysis
clang-tidy-$(CLANG_VERSION) \
--quiet -p build_lint --header-filter="$(CURDIR)/src/.*" src/*.cpp
--quiet -p build --header-filter="$(CURDIR)/src/.*" src/*.cpp

.PHONY: lint
lint: fmt_check lint_cpp ## Run all C++ linters

## Run all C++ linters
lint: lint_format lint_cpp
.PHONY: ci
ci: fmt_check lint_cpp test ## Run all CI checks locally

# CLEAN
## Remove all build and test artifacts
clean: build_clean build_lint_clean test_clean
.PHONY: clean
clean: build_clean test_clean ## Remove all build and test artifacts

# SUBMODULES
## Bump StormLib submodule to latest remote
bump_stormlib:
.PHONY: bump_stormlib
bump_stormlib: ## Bump StormLib submodule to latest remote
@read -rp "[*] Bump StormLib? (y/N) " yn; \
case $$yn in \
[yY] ) git submodule update --init --remote extern/StormLib;; \
* ) echo "[*] Skipping...";; \
esac

## Bump CLI11 submodule to latest remote
bump_cli11:
.PHONY: bump_cli11
bump_cli11: ## Bump CLI11 submodule to latest remote
@read -rp "[*] Bump CLI11? (y/N) " yn; \
case $$yn in \
[yY] ) git submodule update --init --remote extern/CLI11;; \
* ) echo "[*] Skipping...";; \
esac

## Bump all submodules to latest remote
bump_submodules: bump_stormlib bump_cli11
.PHONY: bump_submodules
bump_submodules: bump_stormlib bump_cli11 ## Bump all submodules to latest remote

# GET
.PHONY: get_project_version
get_project_version: ## Print the project version from CMakeLists.txt
@grep -oE 'VERSION [0-9]+\.[0-9]+\.[0-9]+' CMakeLists.txt | grep -oE '[0-9]+\.[0-9]+\.[0-9]+'

.PHONY: get_changelog
get_changelog: ## Print release notes for TAG to stdout (default: latest tag; override with TAG=v1.0.0)
@if [ -z "$(TAG)" ]; then \
echo "Error: no tag resolved. Create a git tag or pass TAG=v1.0.0" >&2; \
exit 1; \
fi
@notes=$$(awk -v tag="$(TAG)" \
'/^## /{if(found)exit; if(index($$0,"## "tag" ")==1 || $$0=="## "tag)found=1; next} found{print}' \
CHANGELOG.md); \
if [ -z "$$notes" ]; then \
echo "Error: no CHANGELOG entry found for $(TAG)" >&2; \
exit 1; \
fi; \
echo "$$notes"

# RELEASE
## Fetch package downloads and update README.md badge
fetch_downloads:
.PHONY: fetch_downloads
fetch_downloads: ## Fetch package downloads and update README.md badge
@DOWNLOADS=$$(curl -s "$(PACKAGE_URL)" \
| grep -A2 "Total downloads" \
| grep -o '<h3 title="[0-9]*">[0-9]*</h3>' \
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# mpqcli

![Build Status](https://img.shields.io/github/actions/workflow/status/TheGrayDot/mpqcli/tag.yml?style=flat) ![Test Status](https://img.shields.io/github/actions/workflow/status/TheGrayDot/mpqcli/tag.yml?style=flat&label=test)
![Build Status](https://img.shields.io/github/actions/workflow/status/thegraydot/mpqcli/tag.yml?style=flat) ![Test Status](https://img.shields.io/github/actions/workflow/status/thegraydot/mpqcli/tag.yml?style=flat&label=test)

![Release Version](https://img.shields.io/github/v/release/TheGrayDot/mpqcli?style=flat)
![Release Version](https://img.shields.io/github/v/release/thegraydot/mpqcli?style=flat)

![Release downloads](https://img.shields.io/github/downloads/thegraydot/mpqcli/total?label=release_downloads) ![Package downloads](https://img.shields.io/badge/package_downloads-647-green)
![Release downloads](https://img.shields.io/github/downloads/thegraydot/mpqcli/total?label=release_downloads) ![Package downloads](https://img.shields.io/badge/package_downloads-845-green)

A command-line tool to create, add, remove, list, extract, read, and verify MPQ archives using the [StormLib library](https://github.com/ladislav-zezula/StormLib).

Expand Down
4 changes: 2 additions & 2 deletions docs/building.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
## Linux

```bash
$ git clone --recursive https://github.com/TheGrayDot/mpqcli.git
$ git clone --recursive https://github.com/thegraydot/mpqcli.git
$ cd mpqcli
$ cmake -B build
$ cmake --build build
Expand All @@ -21,7 +21,7 @@ The `mpqcli` binary will be available in: `./build/bin/mpqcli`
## Windows

```bash
$ git clone --recursive https://github.com/TheGrayDot/mpqcli.git
$ git clone --recursive https://github.com/thegraydot/mpqcli.git
$ cd mpqcli
$ cmake -B build
$ cmake --build build --config Release
Expand Down
2 changes: 1 addition & 1 deletion docs/commands/about.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Name: mpqcli
Version: 0.9.8-041480a92e698514d7938426587e93582b336b7d
Author: Thomas Laurenson
License: MIT
GitHub: https://github.com/TheGrayDot/mpqcli
GitHub: https://github.com/thegraydot/mpqcli
Dependencies:
- StormLib (https://github.com/ladislav-zezula/StormLib)
- CLI11 (https://github.com/CLIUtils/CLI11)
Expand Down
Loading