From a4fc6a158e79e2354bb09eac1154f28d00c04a06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mr=C2=A4KayJayDee?= Date: Thu, 23 Oct 2025 23:42:16 +0200 Subject: [PATCH] feat(build): add release automation tasks and CI/CD workflows - Introduced Gradle tasks for release preparation, version bumping, and git tagging. - Added CI workflow for continuous integration on pushes and pull requests. - Implemented release workflow for automated builds and Gitea releases upon tag pushes. - Created scripts for manual release processes on Windows and Linux/macOS. - Documented the release process and Gitea Actions setup in the new RELEASE.md and setup-gitea-actions.md files. --- .gitea/workflows/ci.yml | 47 +++++++++ .gitea/workflows/release.yml | 89 +++++++++++++++++ build.gradle | 81 +++++++++++++++ docs/RELEASE.md | 168 ++++++++++++++++++++++++++++++++ scripts/release.bat | 77 +++++++++++++++ scripts/release.sh | 74 ++++++++++++++ scripts/setup-gitea-actions.md | 173 +++++++++++++++++++++++++++++++++ 7 files changed, 709 insertions(+) create mode 100644 .gitea/workflows/ci.yml create mode 100644 .gitea/workflows/release.yml create mode 100644 docs/RELEASE.md create mode 100644 scripts/release.bat create mode 100644 scripts/release.sh create mode 100644 scripts/setup-gitea-actions.md diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..f88554a --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,47 @@ +name: CI + +on: + push: + branches: [ main, develop ] + pull_request: + branches: [ main ] + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: Cache Gradle packages + uses: actions/cache@v3 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Make gradlew executable + run: chmod +x ./gradlew + + - name: Run tests + run: ./gradlew test + + - name: Build project + run: ./gradlew build + + - name: Upload build artifacts + uses: actions/upload-artifact@v3 + with: + name: build-artifacts + path: build/libs/*.jar + retention-days: 7 diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml new file mode 100644 index 0000000..1da73bd --- /dev/null +++ b/.gitea/workflows/release.yml @@ -0,0 +1,89 @@ +name: Release Build + +on: + push: + tags: + - 'v*' + workflow_dispatch: + inputs: + version: + description: 'Version to release (e.g., 1.0.1)' + required: true + type: string + +jobs: + build-and-release: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Fetch all history for proper tagging + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: Cache Gradle packages + uses: actions/cache@v3 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Make gradlew executable + run: chmod +x ./gradlew + + - name: Build with Gradle + run: ./gradlew build + + - name: Create Gitea Release + run: | + # Get the tag name from the trigger + TAG_NAME=${GITHUB_REF#refs/tags/} + echo "Creating release for tag: $TAG_NAME" + + # Create release using Gitea API + curl -X POST \ + -H "Authorization: token ${{ secrets.TOKEN }}" \ + -H "Content-Type: application/json" \ + -d "{ + \"tag_name\": \"$TAG_NAME\", + \"name\": \"PlayHours $TAG_NAME\", + \"body\": \"## PlayHours $TAG_NAME\\n\\n### Installation\\n1. Download the JAR file below\\n2. Place it in your server's \`mods\` folder\\n3. Restart your server\\n\\n### Requirements\\n- Minecraft ${{ env.MINECRAFT_VERSION || '1.20.1' }}\\n- Forge ${{ env.FORGE_VERSION || '47.4.10' }}\\n- Java 17+\", + \"draft\": false, + \"prerelease\": false + }" \ + "${{ env.API_URL || 'https://gitea.kamisama.ovh:2222' }}/api/v1/repos/${{ github.repository }}/releases" + + env: + GITEA_TOKEN: ${{ secrets.TOKEN }} + GITEA_API_URL: ${{ secrets.API_URL }} + + - name: Upload JAR to Release + run: | + TAG_NAME=${GITHUB_REF#refs/tags/} + JAR_FILE=$(find build/libs -name "*.jar" | head -1) + + if [ -z "$JAR_FILE" ]; then + echo "No JAR file found in build/libs/" + exit 1 + fi + + echo "Uploading $JAR_FILE to release $TAG_NAME" + + # Upload JAR file to the release + curl -X POST \ + -H "Authorization: token ${{ secrets.TOKEN }}" \ + -F "attachment=@$JAR_FILE" \ + "${{ env.API_URL || 'https://gitea.kamisama.ovh:2222' }}/api/v1/repos/${{ github.repository }}/releases/$TAG_NAME/assets?name=$(basename $JAR_FILE)" + + env: + GITEA_TOKEN: ${{ secrets.TOKEN }} + GITEA_API_URL: ${{ secrets.API_URL }} diff --git a/build.gradle b/build.gradle index 88bdcdb..63477be 100644 --- a/build.gradle +++ b/build.gradle @@ -3,6 +3,7 @@ plugins { id 'idea' id 'maven-publish' id 'net.minecraftforge.gradle' version '[6.0,6.2)' + id 'org.ajoberstar.grgit' version '5.2.0' } version = mod_version @@ -197,3 +198,83 @@ publishing { tasks.withType(JavaCompile).configureEach { options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation } + +// Release automation tasks +task prepareRelease { + dependsOn 'build' + group = 'release' + description = 'Prepares release files and creates git tag' + + doLast { + def tagName = "v${mod_version}" + def jarFile = file("${buildDir}/libs/${mod_id}-${mod_version}.jar") + + if (!jarFile.exists()) { + throw new GradleException("JAR file not found: ${jarFile}") + } + + println "Release prepared:" + println " Tag: ${tagName}" + println " JAR: ${jarFile}" + println "" + println "Next steps:" + println "1. Push the tag: git push origin ${tagName}" + println "2. Create release on Gitea manually or use Gitea Actions" + } +} + +// Task to create a git tag +task createTag { + doLast { + def tagName = "v${mod_version}" + try { + grgit.tag.add(name: tagName, message: "Release ${tagName}") + println "Created tag: ${tagName}" + } catch (Exception e) { + println "Tag ${tagName} might already exist: ${e.message}" + } + } +} + +// Task to push tags +task pushTags { + doLast { + try { + grgit.push(tags: true) + println "Pushed tags to remote" + } catch (Exception e) { + println "Error pushing tags: ${e.message}" + } + } +} + +// Combined release task +task release { + dependsOn 'createTag', 'pushTags', 'prepareRelease' + group = 'release' + description = 'Creates a git tag, pushes it, and prepares for Gitea release' +} + +// Task to bump version +task bumpVersion { + doLast { + def currentVersion = mod_version + def versionParts = currentVersion.split('\\.') + def major = versionParts[0] as Integer + def minor = versionParts[1] as Integer + def patch = versionParts[2] as Integer + + // Increment patch version + patch++ + def newVersion = "${major}.${minor}.${patch}" + + // Update gradle.properties + def propsFile = file('gradle.properties') + def propsContent = propsFile.text + propsContent = propsContent.replaceAll("mod_version=${mod_version}", "mod_version=${newVersion}") + propsFile.text = propsContent + + println "Version bumped from ${currentVersion} to ${newVersion}" + println "Don't forget to commit and push the version change!" + } +} diff --git a/docs/RELEASE.md b/docs/RELEASE.md new file mode 100644 index 0000000..5d4e26a --- /dev/null +++ b/docs/RELEASE.md @@ -0,0 +1,168 @@ +# PlayHours Release Guide + +This guide explains how to create releases for PlayHours, both manually and automatically. + +## Prerequisites + +- Git repository with proper remote configuration +- Gradle build system +- Access to your Gitea instance +- Java 17+ installed + +## Manual Release Process + +### Option 1: Using Release Scripts + +#### Windows +```cmd +scripts\release.bat +``` + +#### Linux/macOS +```bash +./scripts/release.sh +``` + +### Option 2: Using Gradle Tasks + +1. **Bump version** (optional): + ```bash + ./gradlew bumpVersion + ``` + +2. **Create release**: + ```bash + ./gradlew release + ``` + +### Option 3: Manual Steps + +1. **Update version** in `gradle.properties`: + ``` + mod_version=1.0.1 + ``` + +2. **Build the project**: + ```bash + ./gradlew clean build + ``` + +3. **Create and push git tag**: + ```bash + git tag -a v1.0.1 -m "Release v1.0.1" + git push origin main + git push origin v1.0.1 + ``` + +4. **Create release on Gitea**: + - Go to your repository on Gitea + - Click "Releases" → "New Release" + - Select tag `v1.0.1` + - Upload JAR file from `build/libs/playhours-1.0.1.jar` + - Add release notes + +## Automated Release Process + +### Using Gitea Actions + +The project includes automated workflows that trigger on: + +1. **Tag pushes**: When you push a tag starting with `v` (e.g., `v1.0.1`) +2. **Manual dispatch**: Through the Gitea Actions interface + +#### Setting up Gitea Actions + +1. **Enable Actions** in your Gitea repository settings +2. **Configure secrets** (if needed): + - `GITEA_TOKEN`: For Gitea API access + - `GITEA_API_URL`: Your Gitea instance URL (optional, defaults to your current instance) + +#### Workflow Files + +- `.gitea/workflows/ci.yml`: Continuous integration on pushes/PRs +- `.gitea/workflows/release.yml`: Automated release builds + +### Automated Release Steps + +1. **Create and push a tag**: + ```bash + git tag -a v1.0.1 -m "Release v1.0.1" + git push origin v1.0.1 + ``` + +2. **Gitea Actions will automatically**: + - Build the project + - Create a release + - Upload the JAR file + - Generate release notes + +## Version Management + +### Semantic Versioning + +PlayHours uses semantic versioning (MAJOR.MINOR.PATCH): +- **MAJOR**: Breaking changes +- **MINOR**: New features (backward compatible) +- **PATCH**: Bug fixes (backward compatible) + +### Version Bumping + +Use the Gradle task to automatically bump the patch version: +```bash +./gradlew bumpVersion +``` + +For major/minor version changes, manually edit `gradle.properties`. + +## Release Checklist + +Before creating a release: + +- [ ] Update `CHANGELOG.md` with new features/fixes +- [ ] Update version in `gradle.properties` +- [ ] Test the build locally +- [ ] Commit all changes +- [ ] Create and push tag +- [ ] Verify release on Gitea + +## Troubleshooting + +### Common Issues + +1. **Build fails**: Check Java version (17+) and Gradle configuration +2. **Tag already exists**: Delete the tag and recreate, or use a new version +3. **Push fails**: Check git remote configuration and permissions +4. **Actions don't trigger**: Ensure Actions are enabled in repository settings + +### Manual Recovery + +If automated release fails: + +1. Build manually: `./gradlew clean build` +2. Create release manually on Gitea +3. Upload JAR from `build/libs/` + +## Advanced Configuration + +### Custom Release Notes + +Edit the release body template in `build.gradle`: +```gradle +body = """ +## PlayHours v${mod_version} +Your custom release notes here... +""" +``` + +### Custom Gitea Instance + +If your Gitea instance is not at the default URL, set the `GITEA_API_URL` secret: +- Go to repository settings → Secrets +- Add `GITEA_API_URL` with your instance URL (e.g., `https://your-gitea.com`) + +## Security Notes + +- Never commit API tokens or passwords +- Use repository secrets for sensitive data +- Regularly rotate access tokens +- Review release permissions carefully diff --git a/scripts/release.bat b/scripts/release.bat new file mode 100644 index 0000000..e54360a --- /dev/null +++ b/scripts/release.bat @@ -0,0 +1,77 @@ +@echo off +REM PlayHours Release Script for Windows +REM This script automates the release process + +echo Starting PlayHours release process... + +REM Check if we're in a git repository +git status >nul 2>&1 +if %errorlevel% neq 0 ( + echo Error: Not in a git repository + exit /b 1 +) + +REM Check if there are uncommitted changes +git diff --quiet +if %errorlevel% neq 0 ( + echo Warning: You have uncommitted changes. Please commit them first. + echo Do you want to continue anyway? (y/N) + set /p choice= + if /i not "%choice%"=="y" ( + echo Release cancelled. + exit /b 1 + ) +) + +REM Get current version +for /f "tokens=2 delims==" %%a in ('findstr "mod_version=" gradle.properties') do set CURRENT_VERSION=%%a +echo Current version: %CURRENT_VERSION% + +REM Ask for new version +echo Enter new version (current: %CURRENT_VERSION%): +set /p NEW_VERSION= + +if "%NEW_VERSION%"=="" ( + echo No version entered. Using current version. + set NEW_VERSION=%CURRENT_VERSION% +) + +REM Update version in gradle.properties +powershell -Command "(Get-Content gradle.properties) -replace 'mod_version=%CURRENT_VERSION%', 'mod_version=%NEW_VERSION%' | Set-Content gradle.properties" + +REM Build the project +echo Building project... +call gradlew clean build +if %errorlevel% neq 0 ( + echo Build failed! + exit /b 1 +) + +REM Create git tag +echo Creating git tag v%NEW_VERSION%... +git tag -a v%NEW_VERSION% -m "Release v%NEW_VERSION%" +if %errorlevel% neq 0 ( + echo Failed to create tag! + exit /b 1 +) + +REM Push changes and tags +echo Pushing changes and tags... +git push origin main +git push origin v%NEW_VERSION% +if %errorlevel% neq 0 ( + echo Failed to push changes! + exit /b 1 +) + +echo. +echo Release v%NEW_VERSION% completed successfully! +echo. +echo Next steps: +echo 1. Go to your Gitea repository +echo 2. Create a new release with tag v%NEW_VERSION% +echo 3. Upload the JAR file from build\libs\playhours-%NEW_VERSION%.jar +echo 4. Add release notes +echo. +echo JAR file location: build\libs\playhours-%NEW_VERSION%.jar +pause diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100644 index 0000000..72f9d7b --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,74 @@ +#!/bin/bash +# PlayHours Release Script for Linux/macOS +# This script automates the release process + +set -e + +echo "Starting PlayHours release process..." + +# Check if we're in a git repository +if ! git status >/dev/null 2>&1; then + echo "Error: Not in a git repository" + exit 1 +fi + +# Check if there are uncommitted changes +if ! git diff --quiet; then + echo "Warning: You have uncommitted changes. Please commit them first." + read -p "Do you want to continue anyway? (y/N): " choice + if [[ ! "$choice" =~ ^[Yy]$ ]]; then + echo "Release cancelled." + exit 1 + fi +fi + +# Get current version +CURRENT_VERSION=$(grep "mod_version=" gradle.properties | cut -d'=' -f2) +echo "Current version: $CURRENT_VERSION" + +# Ask for new version +read -p "Enter new version (current: $CURRENT_VERSION): " NEW_VERSION + +if [ -z "$NEW_VERSION" ]; then + echo "No version entered. Using current version." + NEW_VERSION=$CURRENT_VERSION +fi + +# Update version in gradle.properties +sed -i "s/mod_version=$CURRENT_VERSION/mod_version=$NEW_VERSION/" gradle.properties + +# Build the project +echo "Building project..." +./gradlew clean build +if [ $? -ne 0 ]; then + echo "Build failed!" + exit 1 +fi + +# Create git tag +echo "Creating git tag v$NEW_VERSION..." +git tag -a "v$NEW_VERSION" -m "Release v$NEW_VERSION" +if [ $? -ne 0 ]; then + echo "Failed to create tag!" + exit 1 +fi + +# Push changes and tags +echo "Pushing changes and tags..." +git push origin main +git push origin "v$NEW_VERSION" +if [ $? -ne 0 ]; then + echo "Failed to push changes!" + exit 1 +fi + +echo "" +echo "Release v$NEW_VERSION completed successfully!" +echo "" +echo "Next steps:" +echo "1. Go to your Gitea repository" +echo "2. Create a new release with tag v$NEW_VERSION" +echo "3. Upload the JAR file from build/libs/playhours-$NEW_VERSION.jar" +echo "4. Add release notes" +echo "" +echo "JAR file location: build/libs/playhours-$NEW_VERSION.jar" diff --git a/scripts/setup-gitea-actions.md b/scripts/setup-gitea-actions.md new file mode 100644 index 0000000..287b320 --- /dev/null +++ b/scripts/setup-gitea-actions.md @@ -0,0 +1,173 @@ +# Setting up Gitea Actions for PlayHours + +This guide will help you configure Gitea Actions for automated releases on your self-hosted Gitea instance. + +## Prerequisites + +- Self-hosted Gitea instance with Actions enabled +- Repository with proper permissions +- Git repository with the PlayHours project + +## Step 1: Enable Actions in Gitea + +1. **Check Gitea version**: Ensure you're running Gitea 1.19+ (Actions support) +2. **Enable Actions globally** (if you're the admin): + - Go to Site Administration → Actions + - Enable "Enable Actions" +3. **Enable Actions for your repository**: + - Go to your repository settings + - Under "Features", enable "Actions" + +## Step 2: Configure Repository Secrets + +1. **Go to repository settings** → **Secrets** +2. **Add the following secrets** (if needed): + +### Required Secrets +- `GITEA_TOKEN`: Your Gitea API token (for Gitea API access) +- `GITEA_API_URL`: Your Gitea instance URL (optional, defaults to current instance) + +### Creating API Tokens + +#### For Gitea Token: +1. Go to your Gitea profile → Settings → Applications +2. Generate new token with `repo` scope +3. Copy the token and add it as `GITEA_TOKEN` secret + +#### For Custom Gitea Instance (optional): +1. Go to repository settings → Secrets +2. Add `GITEA_API_URL` with your instance URL (e.g., `https://your-gitea.com`) + +## Step 3: Verify Workflow Files + +Ensure these files exist in your repository: +- `.gitea/workflows/ci.yml` +- `.gitea/workflows/release.yml` + +## Step 4: Test the Setup + +### Test CI Workflow +1. **Make a small change** to your code +2. **Commit and push** to main branch +3. **Check Actions tab** - you should see a CI workflow running + +### Test Release Workflow +1. **Create a test tag**: + ```bash + git tag -a v1.0.0-test -m "Test release" + git push origin v1.0.0-test + ``` +2. **Check Actions tab** - you should see a release workflow running +3. **Check Releases** - a new release should be created + +## Step 5: Configure Runner (if needed) + +If you don't have runners available, you may need to set up self-hosted runners: + +### Using Docker Runner +1. **Install act** (local Actions runner): + ```bash + # On Linux/macOS + curl https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash + + # On Windows (with Chocolatey) + choco install act-cli + ``` + +2. **Test locally**: + ```bash + act -l # List available workflows + act push # Run push workflow + ``` + +### Using Self-Hosted Runner +1. **Download runner** from your Gitea instance +2. **Configure and register** the runner +3. **Start the runner service** + +## Troubleshooting + +### Common Issues + +1. **Actions not showing up**: + - Check if Actions are enabled in repository settings + - Verify Gitea version supports Actions + +2. **Workflow fails to run**: + - Check runner availability + - Verify workflow syntax + - Check secrets configuration + +3. **Build fails**: + - Verify Java 17 is available + - Check Gradle wrapper permissions + - Review build logs for specific errors + +4. **Release creation fails**: + - Verify API tokens have correct permissions + - Check if release already exists + - Review Gitea API rate limits + +### Debug Steps + +1. **Check Actions logs**: + - Go to Actions tab in your repository + - Click on failed workflow + - Review step-by-step logs + +2. **Test locally**: + ```bash + # Test build + ./gradlew clean build + + # Test with act + act push -v + ``` + +3. **Verify secrets**: + - Check secret names match exactly + - Verify token permissions + - Test API access manually + +## Advanced Configuration + +### Custom Runner Labels +If using self-hosted runners, you can specify runner labels in workflows: + +```yaml +jobs: + build: + runs-on: [self-hosted, linux, java17] +``` + +### Environment Variables +Add environment-specific variables: + +```yaml +env: + JAVA_VERSION: '17' + GRADLE_OPTS: '-Xmx2g' +``` + +### Custom Gitea Instance Configuration +If your Gitea instance is not at the default URL: + +```yaml +env: + GITEA_API_URL: ${{ secrets.GITEA_API_URL }} + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} +``` + +## Security Considerations + +1. **Token Permissions**: Use minimal required permissions for API tokens +2. **Secret Management**: Never commit secrets to repository +3. **Runner Security**: Secure self-hosted runners properly +4. **Access Control**: Limit who can trigger releases + +## Monitoring and Maintenance + +1. **Regular Updates**: Keep Gitea and Actions up to date +2. **Log Monitoring**: Check Actions logs for issues +3. **Token Rotation**: Regularly rotate API tokens +4. **Runner Health**: Monitor runner performance and availability