Release v0.2.10: Update package-lock.json and CI config

This commit is contained in:
Joan
2025-12-30 18:51:21 +01:00
parent 8b31011334
commit 592f38827e
108 changed files with 2755 additions and 1112 deletions

36
scripts/README.md Normal file
View File

@@ -0,0 +1,36 @@
# Release Scripts
## release.sh
Automated release script for Echoes of the Ash desktop builds.
### Usage
```bash
./scripts/release.sh
```
### What it does
1. Generates `package-lock.json` using Docker (no local npm installation required)
2. Suggests next version automatically (increments patch version)
3. Commits changes and pushes to main
4. Creates and pushes git tag to trigger CI/CD pipeline
### Example
```
Current version: v0.2.6
Suggested next version: v0.2.7
Enter version (or press Enter for v0.2.7): v0.3.0
Using version: v0.3.0
✓ Release v0.3.0 completed!
```
The CI/CD pipeline will automatically build:
- Linux AppImage and .deb (compressed in `linux-builds.zip`)
- Windows .exe installer (compressed in `windows-builds.zip`)
Download artifacts from: http://gitlab.kingstudio.es/patacuack/echoes-of-the-ash/-/pipelines

170
scripts/convert_to_i18n.py Normal file
View File

@@ -0,0 +1,170 @@
#!/usr/bin/env python3
"""
Convert game data JSON files to i18n-ready format.
Transforms plain text fields to inline translation objects.
Usage: python convert_to_i18n.py
"""
import json
from pathlib import Path
GAMEDATA_DIR = Path(__file__).parent.parent / 'gamedata'
# Fields that should be translated
TRANSLATABLE_FIELDS = ['name', 'description']
# Nested text fields in outcomes
OUTCOME_TEXT_FIELDS = ['success', 'failure', 'crit_success', 'crit_failure']
def convert_text_to_i18n(value: str) -> dict:
"""Convert a plain string to i18n format"""
if not value or not isinstance(value, str):
return value
return {
"en": value,
"es": "" # Empty - to be translated
}
def convert_item(item_id: str, item: dict) -> dict:
"""Convert an item to i18n format"""
result = {}
for key, value in item.items():
if key in TRANSLATABLE_FIELDS and isinstance(value, str):
result[key] = convert_text_to_i18n(value)
else:
result[key] = value
return result
def convert_location(location: dict) -> dict:
"""Convert a location to i18n format with nested interactable outcomes"""
result = {}
for key, value in location.items():
if key in TRANSLATABLE_FIELDS and isinstance(value, str):
result[key] = convert_text_to_i18n(value)
elif key == 'interactables' and isinstance(value, dict):
# Convert interactable outcome texts
result[key] = convert_interactables(value)
else:
result[key] = value
return result
def convert_interactables(interactables: dict) -> dict:
"""Convert interactable templates and outcomes to i18n format"""
result = {}
for inter_id, inter_data in interactables.items():
result[inter_id] = {}
for key, value in inter_data.items():
if key in TRANSLATABLE_FIELDS and isinstance(value, str):
result[inter_id][key] = convert_text_to_i18n(value)
elif key == 'actions' and isinstance(value, dict):
result[inter_id][key] = convert_actions(value)
elif key == 'outcomes' and isinstance(value, dict):
result[inter_id][key] = convert_outcomes(value)
else:
result[inter_id][key] = value
return result
def convert_actions(actions: dict) -> dict:
"""Convert action labels to i18n format"""
result = {}
for action_id, action_data in actions.items():
result[action_id] = {}
for key, value in action_data.items():
if key == 'label' and isinstance(value, str):
result[action_id][key] = convert_text_to_i18n(value)
else:
result[action_id][key] = value
return result
def convert_outcomes(outcomes: dict) -> dict:
"""Convert outcome text fields to i18n format"""
result = {}
for outcome_id, outcome_data in outcomes.items():
result[outcome_id] = {}
for key, value in outcome_data.items():
if key == 'text' and isinstance(value, dict):
result[outcome_id][key] = {}
for text_key, text_value in value.items():
if text_key in OUTCOME_TEXT_FIELDS and isinstance(text_value, str) and text_value:
result[outcome_id][key][text_key] = convert_text_to_i18n(text_value)
else:
result[outcome_id][key][text_key] = text_value
else:
result[outcome_id][key] = value
return result
def convert_npc(npc: dict) -> dict:
"""Convert an NPC to i18n format"""
result = {}
for key, value in npc.items():
if key in TRANSLATABLE_FIELDS and isinstance(value, str):
result[key] = convert_text_to_i18n(value)
else:
result[key] = value
return result
def convert_interactable_template(interactable: dict) -> dict:
"""Convert an interactable template to i18n format"""
result = {}
for key, value in interactable.items():
if key in TRANSLATABLE_FIELDS and isinstance(value, str):
result[key] = convert_text_to_i18n(value)
elif key == 'actions' and isinstance(value, dict):
result[key] = convert_actions(value)
else:
result[key] = value
return result
def convert_file(filename: str, converter, key_name: str):
"""Convert a JSON file to i18n format"""
filepath = GAMEDATA_DIR / filename
print(f"Converting {filename}...")
with open(filepath, 'r', encoding='utf-8') as f:
data = json.load(f)
if key_name in data:
if isinstance(data[key_name], dict):
# items.json format: {"items": {"item_id": {...}, ...}}
data[key_name] = {
item_id: converter(item_id, item) if key_name == 'items' else converter(item)
for item_id, item in data[key_name].items()
}
elif isinstance(data[key_name], list):
# locations.json format: {"locations": [{...}, ...]}
data[key_name] = [converter(item) for item in data[key_name]]
# Write back
output_file = GAMEDATA_DIR / f'{filename.replace(".json", "_i18n.json")}'
with open(output_file, 'w', encoding='utf-8') as f:
json.dump(data, f, indent=2, ensure_ascii=False)
print(f" -> Saved to {output_file.name}")
def main():
print("Converting game data to i18n format...\n")
convert_file('items.json', convert_item, 'items')
convert_file('locations.json', convert_location, 'locations')
convert_file('npcs.json', convert_npc, 'npcs')
convert_file('interactables.json', convert_interactable_template, 'interactables')
print("\nDone! Review *_i18n.json files, then rename to replace originals.")
print("Spanish translations are empty - add them in the web-map editor.")
if __name__ == '__main__':
main()

118
scripts/release.sh Executable file
View File

@@ -0,0 +1,118 @@
#!/bin/bash
# Echoes of the Ash - Release Script
# Generates package-lock.json, commits changes, and creates a new release tag
set -e # Exit on error
# Colors for output
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m' # No Color
echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE} Echoes of the Ash - Release Script${NC}"
echo -e "${BLUE}========================================${NC}"
echo ""
# Get the project root directory
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$PROJECT_ROOT"
# Step 1: Generate package-lock.json using Docker
echo -e "${YELLOW}[1/5]${NC} Generating package-lock.json with Docker..."
docker run --rm -v "$PROJECT_ROOT/pwa:/app" -w /app node:20-alpine npm install
# Optional: Remove node_modules to keep local directory clean
echo -e "${YELLOW}[2/5]${NC} Cleaning up node_modules..."
#rm -rf "$PROJECT_ROOT/pwa/node_modules"
echo -e "${GREEN}${NC} package-lock.json generated successfully"
echo ""
# Step 2: Get current version from git tags
echo -e "${YELLOW}[3/5]${NC} Determining version..."
CURRENT_VERSION=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
echo -e "Current version: ${GREEN}$CURRENT_VERSION${NC}"
# Parse version and suggest next version
if [[ $CURRENT_VERSION =~ v([0-9]+)\.([0-9]+)\.([0-9]+) ]]; then
MAJOR="${BASH_REMATCH[1]}"
MINOR="${BASH_REMATCH[2]}"
PATCH="${BASH_REMATCH[3]}"
NEXT_PATCH=$((PATCH + 1))
SUGGESTED_VERSION="v${MAJOR}.${MINOR}.${NEXT_PATCH}"
else
SUGGESTED_VERSION="v0.1.0"
fi
echo -e "Suggested next version: ${BLUE}$SUGGESTED_VERSION${NC}"
echo ""
read -p "Enter version (or press Enter for $SUGGESTED_VERSION): " NEW_VERSION
# Use suggested version if user didn't provide one
if [ -z "$NEW_VERSION" ]; then
NEW_VERSION="$SUGGESTED_VERSION"
fi
# Ensure version starts with 'v'
if [[ ! $NEW_VERSION =~ ^v ]]; then
NEW_VERSION="v$NEW_VERSION"
fi
echo -e "Using version: ${GREEN}$NEW_VERSION${NC}"
echo ""
# Step 3: Git add and commit
echo -e "${YELLOW}[4/5]${NC} Committing changes..."
git add -A
# Check if there are changes to commit
if git diff --cached --quiet; then
echo -e "${YELLOW}No changes to commit${NC}"
else
git commit -m "Release $NEW_VERSION: Update package-lock.json and CI config"
echo -e "${GREEN}${NC} Changes committed"
fi
# Push to main
echo -e "${YELLOW}Pushing to main branch...${NC}"
git push origin main
echo -e "${GREEN}${NC} Pushed to main"
echo ""
# Step 4: Create and push tag
echo -e "${YELLOW}[5/5]${NC} Creating release tag..."
# Delete tag if it already exists (locally and remotely)
if git tag -l | grep -q "^$NEW_VERSION$"; then
echo -e "${YELLOW}Tag $NEW_VERSION already exists, deleting...${NC}"
git tag -d "$NEW_VERSION"
git push origin ":refs/tags/$NEW_VERSION" 2>/dev/null || true
fi
# Create new tag
git tag "$NEW_VERSION"
echo -e "${GREEN}${NC} Tag $NEW_VERSION created"
# Push tag
git push origin "$NEW_VERSION"
echo -e "${GREEN}${NC} Tag pushed to remote"
echo ""
# Step 5: Summary
echo -e "${BLUE}========================================${NC}"
echo -e "${GREEN}✓ Release $NEW_VERSION completed!${NC}"
echo -e "${BLUE}========================================${NC}"
echo ""
echo -e "Pipeline URL:"
echo -e "${BLUE}http://gitlab.kingstudio.es/patacuack/echoes-of-the-ash/-/pipelines${NC}"
echo ""
echo -e "The CI/CD pipeline will now build:"
echo -e " • Linux AppImage and .deb (compressed)"
echo -e " • Windows .exe installer (compressed)"
echo ""
echo -e "Build time: ~10-20 minutes"
echo ""