Release v0.2.10: Update package-lock.json and CI config
This commit is contained in:
36
scripts/README.md
Normal file
36
scripts/README.md
Normal 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
170
scripts/convert_to_i18n.py
Normal 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
118
scripts/release.sh
Executable 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 ""
|
||||
Reference in New Issue
Block a user