1.0.2 #18
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |