Skip to content

1.0.2

1.0.2 #18

Workflow file for this run

name: Release Module
on:
release:
types: [published]
permissions:
contents: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
- name: Install Dependencies and Build
run: |
echo "Installing dependencies..."
npm ci
echo "Building module..."
npm run vite:build
- name: Validate Module Manifest
run: |
echo "Validating module.json..."
# Check if module.json exists
if [ ! -f "module.json" ]; then
echo "ERROR: module.json not found!"
exit 1
fi
# Validate JSON syntax
if ! node -e "JSON.parse(require('fs').readFileSync('module.json', 'utf8'))"; then
echo "ERROR: Invalid JSON syntax in module.json!"
exit 1
fi
# Check required fields
node -e "
const manifest = JSON.parse(require('fs').readFileSync('module.json', 'utf8'));
const required = ['id', 'title', 'description', 'version', 'compatibility'];
const missing = required.filter(field => !manifest[field]);
if (missing.length > 0) {
console.log('ERROR: Missing required fields:', missing.join(', '));
process.exit(1);
}
console.log('SUCCESS: Module manifest validation passed');
"
- name: Validate File Structure
run: |
echo "Validating file structure..."
# Check for scripts referenced in module.json
if [ -f "module.json" ]; then
node -e "
const manifest = JSON.parse(require('fs').readFileSync('module.json', 'utf8'));
const fs = require('fs');
let errors = [];
// Check esmodules
if (manifest.esmodules) {
manifest.esmodules.forEach(script => {
if (!fs.existsSync(script)) {
errors.push('Missing esmodule: ' + script);
}
});
}
// Check styles
if (manifest.styles) {
manifest.styles.forEach(style => {
if (!fs.existsSync(style)) {
errors.push('Missing style: ' + style);
}
});
}
// Check languages
if (manifest.languages) {
manifest.languages.forEach(lang => {
if (!fs.existsSync(lang.path)) {
errors.push('Missing language file: ' + lang.path);
}
});
}
if (errors.length > 0) {
console.log('ERROR: File validation errors:');
errors.forEach(err => console.log(' - ' + err));
process.exit(1);
}
console.log('SUCCESS: File structure validation passed');
"
fi
- name: Check Version Consistency
run: |
echo "Checking version consistency..."
RELEASE_VERSION="${{ github.event.release.tag_name }}"
RELEASE_VERSION="${RELEASE_VERSION#v}" # Remove 'v' prefix if present
MANIFEST_VERSION=$(node -e "console.log(JSON.parse(require('fs').readFileSync('module.json', 'utf8')).version)")
if [ "$RELEASE_VERSION" != "$MANIFEST_VERSION" ]; then
echo "ERROR: Version mismatch! Release: $RELEASE_VERSION, Manifest: $MANIFEST_VERSION"
echo "Please ensure the module.json version matches the release tag"
exit 1
fi
echo "SUCCESS: Version consistency check passed: $RELEASE_VERSION"
- name: Generate Enhanced Changelog
id: changelog
run: |
echo "Generating changelog..."
# Get commits since last release
LAST_RELEASE=$(git describe --tags --abbrev=0 HEAD~1 2>/dev/null || echo "")
if [ -n "$LAST_RELEASE" ]; then
COMMITS=$(git log --pretty=format:"- %s" $LAST_RELEASE..HEAD)
else
COMMITS=$(git log --pretty=format:"- %s")
fi
# Create enhanced changelog
CHANGELOG=$(cat << EOF
## What's Changed
$COMMITS
## Installation
Install this module using the following manifest URL:
\`\`\`
https://github.com/${{ github.repository }}/releases/latest/download/module.json
\`\`\`
## Compatibility
- **Minimum Foundry Version**: $(node -e "console.log(JSON.parse(require('fs').readFileSync('module.json', 'utf8')).compatibility.minimum)")
- **Verified Foundry Version**: $(node -e "console.log(JSON.parse(require('fs').readFileSync('module.json', 'utf8')).compatibility.verified)")
---
*This release was automatically generated and validated.*
EOF
)
# Save changelog to file and output
echo "$CHANGELOG" > RELEASE_CHANGELOG.md
echo "changelog<<EOF" >> $GITHUB_OUTPUT
echo "$CHANGELOG" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Update Manifest URLs
run: |
echo "Updating manifest URLs for release..."
# Use sed instead of variable substitution for better control
sed -i "s|\"version\": \".*\"|\"version\": \"${{ github.event.release.tag_name }}\"|g" module.json
sed -i "s|\"url\": \".*\"|\"url\": \"https://github.com/${{ github.repository }}\"|g" module.json
sed -i "s|\"manifest\": \".*\"|\"manifest\": \"https://github.com/${{ github.repository }}/releases/latest/download/module.json\"|g" module.json
sed -i "s|\"download\": \".*\"|\"download\": \"https://github.com/${{ github.repository }}/releases/download/${{ github.event.release.tag_name }}/module.zip\"|g" module.json
echo "SUCCESS: Manifest URLs updated"
- name: Validate Updated Manifest
run: |
echo "Re-validating updated manifest..."
# Validate JSON is still valid after sed operations
if ! node -e "JSON.parse(require('fs').readFileSync('module.json', 'utf8'))"; then
echo "ERROR: Manifest corrupted during URL updates!"
exit 1
fi
echo "SUCCESS: Updated manifest is valid"
- name: Create Module Archive
run: |
echo "Creating module archive..."
# Create zip with proper structure
zip -r ./module.zip \
module.json \
dist/ \
languages/ \
templates/ \
LICENSE \
README.md \
CHANGELOG.md \
-x "*.git*" "node_modules/*" "foundry-data/*" ".env*"
# Verify zip contents
echo "Archive contents:"
unzip -l module.zip
echo "SUCCESS: Module archive created successfully"
- name: Update Release with Assets and Changelog
uses: ncipollo/release-action@v1
with:
allowUpdates: true
name: ${{ github.event.release.name }}
tag: ${{ github.event.release.tag_name }}
body: ${{ steps.changelog.outputs.changelog }}
artifacts: './module.json,./module.zip'
token: ${{ secrets.GITHUB_TOKEN }}
draft: false
prerelease: false
- name: Publish to Foundry VTT Package Release API
if: ${{ !github.event.release.prerelease }}
env:
FOUNDRY_RELEASE_TOKEN: ${{ secrets.FOUNDRY_RELEASE_TOKEN }}
run: |
echo "Publishing to Foundry VTT Package Release API..."
# Check if token is set
if [ -z "$FOUNDRY_RELEASE_TOKEN" ]; then
echo "WARNING: FOUNDRY_RELEASE_TOKEN not set, skipping Foundry API publish"
echo "To enable automatic publishing to Foundry's package listing:"
echo " 1. Get your API key from https://foundryvtt.com/me/api"
echo " 2. Add it as a repository secret named FOUNDRY_RELEASE_TOKEN"
exit 0
fi
# Get the package ID from module.json
PACKAGE_ID=$(node -e "console.log(JSON.parse(require('fs').readFileSync('module.json', 'utf8')).id)")
# Prepare the release data
MANIFEST_URL="https://github.com/${{ github.repository }}/releases/download/${{ github.event.release.tag_name }}/module.json"
RELEASE_VERSION="${{ github.event.release.tag_name }}"
RELEASE_VERSION="${RELEASE_VERSION#v}" # Remove 'v' prefix if present
# Get compatibility info from module.json
COMPATIBILITY=$(node -e "
const manifest = JSON.parse(require('fs').readFileSync('module.json', 'utf8'));
console.log(JSON.stringify({
minimum: manifest.compatibility.minimum,
verified: manifest.compatibility.verified,
maximum: manifest.compatibility.maximum || ''
}));
")
# Extract individual compatibility values
MIN_VERSION=$(echo $COMPATIBILITY | node -e "console.log(JSON.parse(require('fs').readFileSync(0, 'utf8')).minimum)")
VERIFIED_VERSION=$(echo $COMPATIBILITY | node -e "console.log(JSON.parse(require('fs').readFileSync(0, 'utf8')).verified)")
MAX_VERSION=$(echo $COMPATIBILITY | node -e "console.log(JSON.parse(require('fs').readFileSync(0, 'utf8')).maximum || '')")
# Prepare the API request payload
PAYLOAD=$(cat <<EOF
{
"id": "$PACKAGE_ID",
"release": {
"version": "$RELEASE_VERSION",
"manifest": "$MANIFEST_URL",
"notes": "https://github.com/${{ github.repository }}/releases/tag/${{ github.event.release.tag_name }}",
"compatibility": {
"minimum": "$MIN_VERSION",
"verified": "$VERIFIED_VERSION"$([ -n "$MAX_VERSION" ] && echo ",\"maximum\": \"$MAX_VERSION\"" || echo "")
}
}
}
EOF
)
echo "Submitting release to Foundry VTT Package API..."
echo "Package ID: $PACKAGE_ID"
echo "Version: $RELEASE_VERSION"
echo "Manifest URL: $MANIFEST_URL"
# Debug: Check token format (without revealing it)
echo "Token starts with: $(echo $FOUNDRY_RELEASE_TOKEN | cut -c1-6)..."
echo "Token length: ${#FOUNDRY_RELEASE_TOKEN}"
# Submit to Foundry VTT Package Release API
# Try with verbose output to see headers
echo "Sending request to Foundry API..."
RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \
-H "Content-Type: application/json" \
-H "Authorization: ${FOUNDRY_RELEASE_TOKEN}" \
-d "$PAYLOAD" \
https://foundryvtt.com/_api/packages/release_version/)
# Extract HTTP status code and response body
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
RESPONSE_BODY=$(echo "$RESPONSE" | sed '$d')
echo "HTTP Status Code: $HTTP_CODE"
echo "Response Body: $RESPONSE_BODY"
# Check response - look for success indicators or known error patterns
if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "201" ] || echo "$RESPONSE_BODY" | grep -q '"status":\s*"success"'; then
echo "SUCCESS: Package published to Foundry VTT Package Release API!"
elif [ "$HTTP_CODE" = "401" ] || [ "$HTTP_CODE" = "403" ]; then
echo "ERROR: Authentication failed (HTTP $HTTP_CODE)"
echo "Please verify your FOUNDRY_RELEASE_TOKEN is correct"
echo "Get your token from: https://foundryvtt.com/me/api"
exit 1
elif echo "$RESPONSE_BODY" | grep -q "error\|Error\|failed\|Failed"; then
echo "ERROR: Failed to publish to Foundry API"
exit 1
elif [ -z "$RESPONSE_BODY" ] && [ "$HTTP_CODE" != "200" ] && [ "$HTTP_CODE" != "201" ]; then
echo "ERROR: Empty response with HTTP status $HTTP_CODE"
exit 1
else
# If response doesn't clearly indicate success or failure, show it as a warning
echo "WARNING: Unexpected response from Foundry API (HTTP $HTTP_CODE)"
echo "This may indicate success - please check https://foundryvtt.com/packages"
fi
- name: Release Summary
run: |
echo "Release completed successfully!"
echo ""
echo "Release Details:"
echo " - Version: ${{ github.event.release.tag_name }}"
echo " - Repository: ${{ github.repository }}"
echo " - Manifest URL: https://github.com/${{ github.repository }}/releases/latest/download/module.json"
echo " - Download URL: https://github.com/${{ github.repository }}/releases/download/${{ github.event.release.tag_name }}/module.zip"
echo ""
echo "Users can install this module using the manifest URL above."
echo ""
if [ -n "${{ secrets.FOUNDRY_RELEASE_TOKEN }}" ]; then
echo "✅ Module has been published to the Foundry VTT Package Listing!"
else
echo "ℹ️ To auto-publish to Foundry VTT Package Listing, add FOUNDRY_RELEASE_TOKEN secret"
fi