diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c2ec346..2092403 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,11 +1,24 @@ +image: ubuntu:latest + + stages: - - compile + - build - release -# Compile Job (runs on every commit) -compile: - stage: compile - image: ubuntu:latest +variables: + MANIFEST: "system.json" + ZIPFILE: "kidsonbrooms.zip" + PACKAGE_REGISTRY_URL: "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/${CI_PROJECT_NAME}/${CI_COMMIT_TAG}" + MANIFEST_RELEASE_URL: "${PACKAGE_REGISTRY_URL}/${MANIFEST}" + ZIPFILE_RELEASE_URL: "${PACKAGE_REGISTRY_URL}/${ZIPFILE}" + MANIFEST_PERMALINK_URL: "https://gitlab.com/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}/-/releases/permalink/latest/downloads/${MANIFEST}" + ZIPFILE_PERMALINK_URL: "https://gitlab.com/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}/-/releases/${CI_COMMIT_TAG}/downloads/${ZIPFILE}" + dry_run: true + + +# Build job +build: + stage: build before_script: # Install Node.js v21.x manually - apt-get update && apt-get install -y curl @@ -17,94 +30,57 @@ compile: - gulp --version # Verify Gulp is installed script: - npm install - - gulp compile - only: - - branches + - gulp build artifacts: paths: - kidsonbrooms.zip - expire_in: never + - system.json + - packs/ + only: + - branches -# Release Job (manually triggered with version) +# Release job release: stage: release - image: ubuntu:latest + rules: + - if: $CI_COMMIT_TAG before_script: - # Install necessary tools - - apt-get update && apt-get install -y curl jq git # Install Node.js v21.x manually + - apt-get update && apt-get install -y curl - curl -fsSL https://deb.nodesource.com/setup_21.x | bash - - apt-get install -y nodejs - node -v # Verify the correct Node.js version # Install Gulp globally - npm install --global gulp-cli - gulp --version # Verify Gulp is installed - - git fetch --all - - git switch master - - git branch --set-upstream-to=origin/master master script: - # Check if VERSION is provided - - | - if [ -z "$VERSION" ]; then - echo "Error: VERSION variable is required." - exit 1 - fi - - # Install dependencies and run Gulp release task - npm install - gulp release + only: + - tags - - grep '"download":' system.json - - git config --global user.name "GitLab CI" - - git config --global user.email "ci@gitlab.com" - - git add kidsonbrooms.zip - - git commit -m "Update .zip with new version" - - git push "https://$CI_COMMITTER_USER_AND_TOKEN@gitlab.com/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}.git" HEAD:master +# Create GitLab release +create-release: + stage: release + image: registry.gitlab.com/gitlab-org/release-cli:latest + variables: + dry_run: "false" + needs: + - job: release + rules: + - if: $CI_COMMIT_TAG + script: + - echo "Creating GitLab release for $CI_COMMIT_TAG" + release: + name: "$CI_COMMIT_TAG" + tag_name: "$CI_COMMIT_TAG" + description: "Release $CI_COMMIT_TAG of $CI_PROJECT_NAME." + assets: + links: + - name: "$MANIFEST" + url: "${MANIFEST_RELEASE_URL}" + filepath: "/${MANIFEST}" + - name: "$ZIPFILE" + url: "${ZIPFILE_PERMALINK_URL}" + filepath: "/${ZIPFILE}" - # Create a release on GitLab - - | - export RELEASE_RESPONSE=$(curl --request POST \ - --header "PRIVATE-TOKEN: ${GITLAB_PAT}" \ - --header "Content-Type: application/json" \ - --data '{ - "name": "Release v'$VERSION'", - "tag_name": "v'$VERSION'", - "description": "Release v'$VERSION'", - "ref": "master", - "assets": { - "links": [ - { - "name": "Download kidsonbrooms.zip", - "url": "https://gitlab.com/wintermyst/kidsonbrooms/-/raw/master/kidsonbrooms.zip?inline=false" - } - ] - } - }' "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/releases") - # Publish the release to the Foundry API - - | - curl -X POST https://api.foundryvtt.com/_api/packages/release_version/ \ - -H "Authorization: $FOUNDRY_API_KEY" \ - -H "Content-Type: application/json" \ - -d "{ - \"id\": \"kidsonbrooms\", - \"release\": { - \"version\": \"$VERSION\", - \"manifest\": \"https://gitlab.com/wintermyst/kidsonbrooms/-/raw/master/system.json\", - \"notes\": \"https://gitlab.com/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}/-/releases/v$VERSION\", - \"compatibility\": { - \"minimum\": \"12\", - \"verified\": \"12.331\", - \"maximum\": \"\" - } - } - }" - only: - - master - when: manual - allow_failure: false - dependencies: - - compile - artifacts: - paths: - - kidsonbrooms.zip - expire_in: never diff --git a/gulpfile.js b/gulpfile.js index a31780f..302e1a8 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -3,6 +3,42 @@ const prefix = require('gulp-autoprefixer'); const sourcemaps = require('gulp-sourcemaps'); const sass = require('gulp-sass')(require('sass')); const zip = require('gulp-zip'); +const fs = require('fs'); +const fetch = require('node-fetch'); +const replace = require('gulp-replace'); +const FormData = require('form-data'); + +/* ----------------------------------------- */ +/* Export Tasks +/* ----------------------------------------- */ + +exports.default = gulp.series( + compileScss, + watchUpdates +); + +exports.build = gulp.series( + compileScss, + checkVersion, + ensureOutputDirExists, + packageCompendiums, + updateSystemJson, + zipRelease +); + +exports.compile = gulp.series( + compileScss, + ensureOutputDirExists, + packageCompendiums, +); + +exports.css = css; + +exports.release = gulp.series( + exports.build, + uploadToPackageRegistry, + publishToFoundry +); /* ----------------------------------------- */ /* Compile Sass @@ -40,21 +76,6 @@ function watchUpdates() { gulp.watch(SYSTEM_SCSS, css); } -/* ----------------------------------------- */ -/* Export Tasks -/* ----------------------------------------- */ - -exports.default = gulp.series( - compileScss, - watchUpdates -); -exports.build = gulp.series( - compileScss -); -exports.compile = gulp.series( - compileScss -); -exports.css = css; /* ----------------------------------------- */ /* Zip Release @@ -71,12 +92,239 @@ function zipRelease() { '!./package.json', '!./scss/**/*', '!./.github/**/*', + '!./.gitlab-ci.yml', + '!./README.md', + '!./compendiums/**/*', + '!./*.zip' ], { base: '.' }) .pipe(zip('kidsonbrooms.zip')) .pipe(gulp.dest('.')); } -exports.release = gulp.series( - compileScss, - zipRelease -); +/* ----------------------------------------- */ +/* Version Check +/* ----------------------------------------- */ + + +function checkVersion(done) { + const moduleManifest = JSON.parse(fs.readFileSync('module.json')); + const manifestVersion = moduleManifest.version; + const gitTag = process.env.CI_COMMIT_TAG; + + if (gitTag && manifestVersion !== gitTag) { + console.error(`Version mismatch between tag (${gitTag}) and manifest (${manifestVersion})!`); + process.exit(1); + } else { + console.log(`Version check passed: ${manifestVersion}`); + done(); + } +} + +/* ----------------------------------------- */ +/* Bundle Compendium +/* ----------------------------------------- */ + +const { exec } = require('child_process'); + +function packageCompendiums(done) { + const packsDir = './compendiums'; // Adjust to your compendium source directory + const outputDir = './packs'; + const moduleId = 'kidsonbrooms'; // Replace with your actual module ID + + // Read all subdirectories in the packsDir + fs.readdir(packsDir, (err, files) => { + if (err) { + console.error(`Error reading directory ${packsDir}: ${err}`); + process.exit(1); + } + + // Filter out files to get only directories + const folders = files.filter(file => fs.statSync(path.join(packsDir, file)).isDirectory()); + + if (folders.length === 0) { + console.log(`No compendium folders found in ${packsDir}`); + done(); + return; + } + + let completed = 0; + folders.forEach(folder => { + const packName = folder; // Use the folder name as the pack name + const inputPath = path.join(packsDir, folder); + const command = `npx fvtt package pack --type System --id ${moduleId} -n "${packName}" --in "${inputPath}" --out "${outputDir}" --yaml`; + + exec(command, (err, stdout, stderr) => { + if (err) { + console.error(`Error packaging compendium ${packName}:\n${stderr}`); + process.exit(1); + } else { + console.log(`Compendium ${packName} packaged successfully.`); + console.log(stdout); + completed++; + // When all compendiums have been processed, call done() + if (completed === folders.length) { + done(); + } + } + }); + }); + }); +} + +/* ----------------------------------------- */ +/* Ensure Output Directory Exists +/* ----------------------------------------- */ + +function ensureOutputDirExists() { + const outputDir = './packs'; + if (!fs.existsSync(outputDir)) { + fs.mkdirSync(outputDir); + } + return Promise.resolve(); +} + +/* ----------------------------------------- */ +/* Upload to Package Registry +/* ----------------------------------------- */ + + +async function uploadToPackageRegistry(done) { + const manifestFile = 'system.json'; + const zipFile = process.env.ZIPFILE || 'kidsonbrooms.zip'; + const packageRegistryUrl = process.env.PACKAGE_REGISTRY_URL; + const ciJobToken = process.env.CI_JOB_TOKEN; + + if (!packageRegistryUrl || !ciJobToken) { + console.error('PACKAGE_REGISTRY_URL or CI_JOB_TOKEN is not defined.'); + process.exit(1); + } + + try { + // Upload manifest file + const manifestUploadUrl = `${packageRegistryUrl}/${manifestFile}`; + console.log(`Uploading ${manifestFile} to ${manifestUploadUrl}`); + + let response = await fetch(manifestUploadUrl, { + method: 'PUT', + headers: { + 'JOB-TOKEN': ciJobToken, + 'Content-Type': 'application/octet-stream', + }, + body: fs.createReadStream(manifestFile), + }); + + if (response.ok) { + console.log(`Uploaded ${manifestFile} successfully.`); + } else { + console.error(`Failed to upload ${manifestFile}: ${response.statusText}`); + process.exit(1); + } + + // Upload zip file + const zipUploadUrl = `${packageRegistryUrl}/${zipFile}`; + console.log(`Uploading ${zipFile} to ${zipUploadUrl}`); + + response = await fetch(zipUploadUrl, { + method: 'PUT', + headers: { + 'JOB-TOKEN': ciJobToken, + 'Content-Type': 'application/octet-stream', + }, + body: fs.createReadStream(zipFile), + }); + + if (response.ok) { + console.log(`Uploaded ${zipFile} successfully.`); + } else { + console.error(`Failed to upload ${zipFile}: ${response.statusText}`); + process.exit(1); + } + + done(); + } catch (error) { + console.error(`Error uploading files: ${error.message}`); + process.exit(1); + } +} + +/* ----------------------------------------- */ +/* Publish to FoundryVTT +/* ----------------------------------------- */ + +function publishToFoundry(done) { + const moduleManifestPath = 'system.json'; + const moduleManifest = JSON.parse(fs.readFileSync(moduleManifestPath)); + + const id = moduleManifest.name; + const version = moduleManifest.version; + const compMin = moduleManifest.compatibility.minimum; + const compVer = moduleManifest.compatibility.verified; + const compMax = moduleManifest.compatibility.maximum; + const manifest = process.env.MANIFEST_RELEASE_URL || `https://gitlab.com/${process.env.CI_PROJECT_NAMESPACE}/${process.env.CI_PROJECT_NAME}/-/releases/${process.env.CI_COMMIT_TAG}/downloads/${moduleManifestPath}`; + const notes = `https://gitlab.com/${process.env.CI_PROJECT_NAMESPACE}/${process.env.CI_PROJECT_NAME}/-/tags/${process.env.CI_COMMIT_TAG}`; + + const dryRun = process.env.dry_run === 'true'; + const authToken = process.env.auth_token; + + if (!authToken) { + console.error('Foundry VTT API authentication token (auth_token) is not defined.'); + process.exit(1); + } + + // Construct the payload + const payload = { + id: id, + release: { + version: version, + manifest: manifest, + notes: notes, + compatibility: { + minimum: compMin, + verified: compVer, + maximum: compMax, + }, + }, + }; + + if (dryRun) { + payload['dry-run'] = true; + } + + // Send the POST request to Foundry VTT API + const response = fetch('https://api.foundryvtt.com/_api/packages/release', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: authToken, + }, + body: JSON.stringify(payload), + }); + + const responseData = response.json(); + + if (responseData.status === 'success') { + console.log('Successfully published to Foundry VTT:'); + console.log(JSON.stringify(responseData, null, 2)); + done(); + } else { + console.error('Failed to publish to Foundry VTT:'); + console.error(JSON.stringify(responseData, null, 2)); + process.exit(1); + } +} + +/* ----------------------------------------- */ +/* Update systen.json with Download URL +/* ----------------------------------------- */ + +function updateSystemJson(done) { + const ManifestPath = 'system.json'; + const Manifest = JSON.parse(fs.readFileSync(ManifestPath)); + const zipUrl = process.env.ZIPFILE_PERMALINK_URL || 'https://gitlab.com/wintermyst/kidsonbrooms/-/raw/master/kidsonbrooms.zip?inline=false'; + + Manifest.download = zipUrl; + + fs.writeFileSync(ManifestPath, JSON.stringify(Manifest, null, 2)); + console.log(`Updated module.json with download URL: ${zipUrl}`); + done(); +} diff --git a/package.json b/package.json index 811ae5d..45bf26c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "kids-on-brooms", - "version": "2.0.0", + "version": "1.1.0", "description": "CSS compiler for the Kids On Brooms system", "scripts": { "build": "gulp build", @@ -20,7 +20,6 @@ "gulp-sass": "^5", "gulp-sourcemaps": "^2.6.5", "gulp-zip": "^5.0.1", - "kids-on-brooms": "file:" }, "devDependencies": { "sass": "^1.79.1"