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
11 changes: 11 additions & 0 deletions .github/workflows/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<claude-mem-context>
# Recent Activity

<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->

### Jan 13, 2026

| ID | Time | T | Title | Read |
|----|------|---|-------|------|
| #167 | 10:49 PM | 🟣 | Test Automation Orchestrator for Themer-Up | ~817 |
Comment on lines +4 to +10
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The date "Jan 13, 2026" is valid and not in the future. However, this appears to be an auto-generated file from the claude-mem tool. Consider whether these metadata files should be included in version control, or if they should be added to .gitignore instead.

Suggested change
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
### Jan 13, 2026
| ID | Time | T | Title | Read |
|----|------|---|-------|------|
| #167 | 10:49 PM | 🟣 | Test Automation Orchestrator for Themer-Up | ~817 |
<!--
This section is reserved for claude-mem and is intentionally left empty
in version control. Local runs of claude-mem may populate this block,
but the generated metadata should not be committed.
-->

Copilot uses AI. Check for mistakes.
</claude-mem-context>
206 changes: 206 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
name: CI Pipeline

on:
push:
branches: [main, develop]
pull_request:
branches: [main]

# Cancel in-progress runs for the same branch
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
BATS_VERSION: "1.10.0"

jobs:
# ============================================
# Stage 1: Static Analysis (runs in parallel)
# ============================================

shellcheck:
name: ShellCheck Linting
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Run ShellCheck
uses: ludeeus/action-shellcheck@master
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ShellCheck action uses @master which is not a pinned version. For better reproducibility and security, consider pinning to a specific version tag or commit SHA instead of using a floating reference like master.

Suggested change
uses: ludeeus/action-shellcheck@master
uses: ludeeus/action-shellcheck@2.0.0

Copilot uses AI. Check for mistakes.
with:
scandir: './scripts'
format: gcc
severity: warning

validate-json:
name: Validate JSON Configs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Validate theme JSON files
run: |
echo "Validating JSON files..."
for file in themes/*/iterm2.json themes/*/vscode.json themes/*/theme.json; do
if [[ -f "$file" ]]; then
echo "Checking $file"
python3 -m json.tool "$file" > /dev/null || exit 1
fi
done
echo "All JSON files valid!"

validate-yaml:
name: Validate YAML Configs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: Install yamllint
run: pip install yamllint

- name: Validate YAML files
run: |
for file in themes/*/*.yaml; do
if [[ -f "$file" ]]; then
echo "Checking $file"
yamllint -d relaxed "$file" || exit 1
fi
done
echo "All YAML files valid!"

# ============================================
# Stage 2: Unit Tests (parallel by script)
# ============================================

unit-tests:
name: Unit Tests
runs-on: macos-latest
needs: [shellcheck] # Only run if linting passes
strategy:
fail-fast: false
matrix:
test-file:
- backup
- restore
- apply
steps:
- uses: actions/checkout@v4

- name: Cache BATS installation
uses: actions/cache@v4
id: bats-cache
with:
path: ~/.local/bats
key: bats-${{ env.BATS_VERSION }}-macos

- name: Install BATS
if: steps.bats-cache.outputs.cache-hit != 'true'
run: |
git clone https://github.com/bats-core/bats-core.git
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The BATS installation clones the repository without specifying a branch or tag. This could lead to inconsistent behavior if the default branch changes. Consider checking out a specific version tag to ensure reproducible builds: git clone --branch v1.10.0 --depth 1 https://github.com/bats-core/bats-core.git

Suggested change
git clone https://github.com/bats-core/bats-core.git
git clone --branch "v${BATS_VERSION}" --depth 1 https://github.com/bats-core/bats-core.git

Copilot uses AI. Check for mistakes.
cd bats-core
./install.sh ~/.local/bats

- name: Add BATS to PATH
run: echo "$HOME/.local/bats/bin" >> "$GITHUB_PATH"

- name: Run unit tests
env:
TEST_FILE: ${{ matrix.test-file }}
run: |
bats "tests/unit/${TEST_FILE}.bats" --tap

# ============================================
# Stage 3: Integration Tests (after unit tests)
# ============================================

integration-tests:
name: Integration Tests
runs-on: macos-latest
needs: [unit-tests] # Only run if unit tests pass
steps:
- uses: actions/checkout@v4

- name: Cache BATS installation
uses: actions/cache@v4
id: bats-cache
with:
path: ~/.local/bats
key: bats-${{ env.BATS_VERSION }}-macos

- name: Install BATS
if: steps.bats-cache.outputs.cache-hit != 'true'
run: |
git clone https://github.com/bats-core/bats-core.git
cd bats-core
./install.sh ~/.local/bats
Comment on lines +138 to +140
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The BATS installation clones the repository without specifying a branch or tag. This could lead to inconsistent behavior if the default branch changes. Consider checking out a specific version tag to ensure reproducible builds: git clone --branch v1.10.0 --depth 1 https://github.com/bats-core/bats-core.git

Copilot uses AI. Check for mistakes.

- name: Add BATS to PATH
run: echo "$HOME/.local/bats/bin" >> "$GITHUB_PATH"

- name: Run integration tests
run: |
bats tests/integration/*.bats --tap

# ============================================
# Stage 4: Theme Validation (parallel check)
# ============================================

theme-validation:
name: Validate Themes
runs-on: ubuntu-latest
needs: [validate-json, validate-yaml]
steps:
- uses: actions/checkout@v4

- name: Check theme completeness
run: |
REQUIRED_FILES=("iterm2.json" "p10k.zsh" "mprocs.yaml" "theme.json")

for theme_dir in themes/*/; do
theme_name=$(basename "$theme_dir")
echo "Validating theme: $theme_name"

for file in "${REQUIRED_FILES[@]}"; do
if [[ ! -f "${theme_dir}${file}" ]]; then
echo "ERROR: Missing $file in $theme_name"
exit 1
fi
done

echo " All required files present"
done

# ============================================
# Final: Summary Job
# ============================================

ci-success:
name: CI Success
runs-on: ubuntu-latest
needs: [shellcheck, validate-json, validate-yaml, unit-tests, integration-tests, theme-validation]
if: always()
steps:
- name: Check all jobs passed
env:
SHELLCHECK_RESULT: ${{ needs.shellcheck.result }}
JSON_RESULT: ${{ needs.validate-json.result }}
YAML_RESULT: ${{ needs.validate-yaml.result }}
UNIT_RESULT: ${{ needs.unit-tests.result }}
INTEGRATION_RESULT: ${{ needs.integration-tests.result }}
THEME_RESULT: ${{ needs.theme-validation.result }}
run: |
if [[ "$SHELLCHECK_RESULT" != "success" ]] || \
[[ "$JSON_RESULT" != "success" ]] || \
[[ "$YAML_RESULT" != "success" ]] || \
[[ "$UNIT_RESULT" != "success" ]] || \
[[ "$INTEGRATION_RESULT" != "success" ]] || \
[[ "$THEME_RESULT" != "success" ]]; then
echo "One or more jobs failed"
exit 1
fi
echo "All CI checks passed!"
18 changes: 18 additions & 0 deletions .shellcheckrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# ShellCheck configuration for themer-up
# https://www.shellcheck.net/wiki/

# Set severity to warning (ignore style/info suggestions)
# Style issues (SC2250, SC2292) are valid suggestions but not errors
severity=warning

# Exclude warnings that don't apply to our use case
# SC2312: Consider invoking this command separately (we handle exit codes)
# SC2012: Use find instead of ls (our use case is controlled)
# SC2129: Consider using grouped redirects (readability preference)
exclude=SC2312,SC2012,SC2129
Comment on lines +10 to +12
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SC2012 exclusion comment states "our use case is controlled" but the scripts use ls -dt with pipe to head and tail which could be problematic with filenames containing newlines or special characters. While this may work in the controlled test environment, consider using find with -printf or other safer alternatives for production code to avoid potential issues with special characters in backup directory names.

Suggested change
# SC2012: Use find instead of ls (our use case is controlled)
# SC2129: Consider using grouped redirects (readability preference)
exclude=SC2312,SC2012,SC2129
# SC2129: Consider using grouped redirects (readability preference)
exclude=SC2312,SC2129

Copilot uses AI. Check for mistakes.

# Set default shell dialect
shell=bash

# External sources (for sourced files)
external-sources=true
126 changes: 126 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# Themer-Up Makefile
# Orchestrates testing, linting, and development tasks

.PHONY: all test test-unit test-integration lint clean install-deps help

# Default target
all: lint test

# ─────────────────────────────────────────────────────
# Testing Targets
# ─────────────────────────────────────────────────────

## Run all tests (linting + unit + integration)
test: lint test-unit test-integration

## Run unit tests only (fast feedback)
test-unit:
@echo "Running unit tests..."
@bats tests/unit/*.bats --tap

## Run integration tests only
test-integration:
@echo "Running integration tests..."
@bats tests/integration/*.bats --tap

## Run tests in parallel (requires GNU parallel)
test-parallel:
@./scripts/test.sh --parallel

## Run tests with verbose output
test-verbose:
@./scripts/test.sh --verbose

## Run fast tests only (skip integration)
test-fast:
@./scripts/test.sh --fast

# ─────────────────────────────────────────────────────
# Linting Targets
# ─────────────────────────────────────────────────────

## Run shellcheck on all scripts
lint:
@./scripts/lint.sh

## Validate all JSON config files
lint-json:
@echo "Validating JSON files..."
@for f in themes/*/iterm2.json themes/*/vscode.json themes/*/theme.json; do \
if [ -f "$$f" ]; then python3 -m json.tool "$$f" > /dev/null && echo " ✓ $$f"; fi; \
done

## Validate all YAML config files
lint-yaml:
@echo "Validating YAML files..."
@for f in themes/*/*.yaml; do \
if [ -f "$$f" ]; then yamllint -d relaxed "$$f" && echo " ✓ $$f"; fi; \
done

# ─────────────────────────────────────────────────────
# Development Targets
# ─────────────────────────────────────────────────────

## Install development dependencies
install-deps:
@echo "Installing dependencies..."
@which bats > /dev/null || brew install bats-core
@which shellcheck > /dev/null || brew install shellcheck
@which yamllint > /dev/null || pip install yamllint
@echo "Done!"

## Apply synthwave theme (default)
apply:
@./scripts/apply.sh synthwave

## Create a backup of current configs
backup:
@./scripts/backup.sh

## Restore from latest backup
restore:
@./scripts/restore.sh

## Clean temporary test files
clean:
@echo "Cleaning temporary files..."
@rm -rf /tmp/themer-test-*
@rm -f /tmp/bats_output
@echo "Done!"

# ─────────────────────────────────────────────────────
# CI Simulation
# ─────────────────────────────────────────────────────

## Simulate full CI pipeline locally
ci: lint lint-json test-unit test-integration
@echo ""
@echo "✅ CI simulation passed!"

# ─────────────────────────────────────────────────────
# Help
# ─────────────────────────────────────────────────────

## Show this help message
help:
@echo "Themer-Up Development Commands"
@echo "══════════════════════════════"
@echo ""
@grep -E '^##' Makefile | sed 's/## / /'
@echo ""
@echo "Usage: make <target>"
@echo ""
@echo "Testing:"
@echo " make test - Run all tests"
@echo " make test-unit - Run unit tests only"
@echo " make test-fast - Skip integration tests"
@echo " make test-parallel - Run tests in parallel"
@echo ""
@echo "Linting:"
@echo " make lint - Run shellcheck"
@echo " make lint-json - Validate JSON files"
@echo ""
@echo "Development:"
@echo " make install-deps - Install dev dependencies"
@echo " make apply - Apply synthwave theme"
@echo " make ci - Simulate CI locally"
3 changes: 2 additions & 1 deletion scripts/apply.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

set -e

THEMER_DIR="$HOME/dev/themer-up"
# Allow override via environment variable for testing and custom installs
THEMER_DIR="${THEMER_DIR:-$HOME/dev/themer-up}"
THEME_NAME="${1:-synthwave}"
THEME_DIR="$THEMER_DIR/themes/$THEME_NAME"

Expand Down
6 changes: 4 additions & 2 deletions scripts/backup.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#!/bin/bash
# Backup current theme configs before applying new theme

BACKUP_DIR="$HOME/.themer-up-backup/$(date +%Y%m%d_%H%M%S)"
# Support custom backup location via environment variable
BACKUP_BASE="${THEMER_BACKUP_DIR:-$HOME/.themer-up-backup}"
BACKUP_DIR="${BACKUP_BASE}/$(date +%Y%m%d_%H%M%S)"
mkdir -p "$BACKUP_DIR"

echo "Backing up to: $BACKUP_DIR"
Expand Down Expand Up @@ -43,6 +45,6 @@ if [ -f "$HOME/.tmux.conf" ]; then
fi

# Keep only last 5 backups
ls -dt "$HOME/.themer-up-backup"/*/ 2>/dev/null | tail -n +6 | xargs rm -rf 2>/dev/null || true
ls -dt "${BACKUP_BASE}"/*/ 2>/dev/null | tail -n +6 | xargs rm -rf 2>/dev/null || true

echo "Backup complete."
Loading
Loading