name: Version and Release on: workflow_dispatch: jobs: version-bump: runs-on: ubuntu-latest outputs: new_version: ${{ steps.version.outputs.new_version }} version_changed: ${{ steps.version.outputs.version_changed }} component_changed: ${{ steps.version.outputs.component_changed }} steps: - uses: actions/checkout@v3 with: fetch-depth: 0 token: ${{ secrets.GITEA_TOKEN || github.token }} - name: Setup Node.js uses: actions/setup-node@v3 with: node-version: '18' - name: Configure Git run: | git config --global user.name 'Gitea Actions Bot' git config --global user.email 'actions@gitea.local' - name: Detect changes and bump version id: version run: | set -e # Exit on error echo "=== Debug Info ===" echo "GitHub event before: ${{ github.event.before }}" echo "GitHub SHA: ${{ github.sha }}" echo "Current directory: $(pwd)" echo "Git log (last 5): $(git log --oneline -5)" # Get the commit range for changed files if [ "${{ github.event.before }}" != "0000000000000000000000000000000000000000" ] && [ "${{ github.event.before }}" != "" ]; then COMMIT_RANGE="${{ github.event.before }}..${{ github.sha }}" echo "Using commit range: $COMMIT_RANGE" CHANGED_FILES=$(git diff --name-only $COMMIT_RANGE || echo "") else # First commit or no previous commit, check against HEAD~1 if it exists if git rev-parse HEAD~1 >/dev/null 2>&1; then COMMIT_RANGE="HEAD~1..HEAD" echo "Using commit range: $COMMIT_RANGE" CHANGED_FILES=$(git diff --name-only $COMMIT_RANGE || echo "") else echo "First commit detected, checking all files" CHANGED_FILES=$(git ls-files) fi fi echo "Changed files:" echo "$CHANGED_FILES" # Check what changed (using echo to pipe to grep to avoid grep exit codes) BACKEND_CHANGED=$(echo "$CHANGED_FILES" | grep -c '^backend/' || echo "0") FRONTEND_CHANGED=$(echo "$CHANGED_FILES" | grep -c '^frontend/' || echo "0") ROOT_CHANGED=$(echo "$CHANGED_FILES" | grep -c -E '^(package\.json|docker-compose|Dockerfile|scripts/)' || echo "0") echo "Backend files changed: $BACKEND_CHANGED" echo "Frontend files changed: $FRONTEND_CHANGED" echo "Root files changed: $ROOT_CHANGED" # Get current versions BACKEND_VERSION=$(node -p "require('./backend/package.json').version" 2>/dev/null || echo "1.1.0") FRONTEND_VERSION=$(node -p "require('./frontend/package.json').version" 2>/dev/null || echo "1.1.0") echo "Current backend version: $BACKEND_VERSION" echo "Current frontend version: $FRONTEND_VERSION" # Determine what to update based on changes BACKEND_UPDATE=false FRONTEND_UPDATE=false COMPONENT_CHANGED="none" if [ "$ROOT_CHANGED" -gt 0 ]; then # Root changes affect both components BACKEND_UPDATE=true FRONTEND_UPDATE=true COMPONENT_CHANGED="both" SOURCE_VERSION=$BACKEND_VERSION echo "Root changes detected - updating both components" elif [ "$BACKEND_CHANGED" -gt 0 ] && [ "$FRONTEND_CHANGED" -gt 0 ]; then # Both components changed BACKEND_UPDATE=true FRONTEND_UPDATE=true COMPONENT_CHANGED="both" # Use the higher version as source if [ "$(printf '%s\n' "$BACKEND_VERSION" "$FRONTEND_VERSION" | sort -V | tail -n1)" = "$BACKEND_VERSION" ]; then SOURCE_VERSION=$BACKEND_VERSION else SOURCE_VERSION=$FRONTEND_VERSION fi echo "Both backend and frontend changed - updating both" elif [ "$BACKEND_CHANGED" -gt 0 ]; then # Only backend changed BACKEND_UPDATE=true COMPONENT_CHANGED="backend" SOURCE_VERSION=$BACKEND_VERSION echo "Only backend changed - updating backend" elif [ "$FRONTEND_CHANGED" -gt 0 ]; then # Only frontend changed FRONTEND_UPDATE=true COMPONENT_CHANGED="frontend" SOURCE_VERSION=$FRONTEND_VERSION echo "Only frontend changed - updating frontend" else echo "No relevant changes detected" echo "version_changed=false" >> $GITHUB_OUTPUT echo "component_changed=none" >> $GITHUB_OUTPUT echo "new_version=" >> $GITHUB_OUTPUT exit 0 fi echo "Component changed: $COMPONENT_CHANGED" echo "Source version: $SOURCE_VERSION" echo "Backend update: $BACKEND_UPDATE" echo "Frontend update: $FRONTEND_UPDATE" # Calculate new version IFS='.' read -r -a version_parts <<< "$SOURCE_VERSION" MAJOR="${version_parts[0]}" MINOR="${version_parts[1]}" PATCH="${version_parts[2]}" # Increment patch version and ensure tag uniqueness git fetch --tags --quiet || true NEW_PATCH=$((PATCH + 1)) NEW_VERSION="$MAJOR.$MINOR.$NEW_PATCH" while git rev-parse "v${NEW_VERSION}" >/dev/null 2>&1; do echo "Tag v${NEW_VERSION} already exists, bumping patch version again" NEW_PATCH=$((NEW_PATCH + 1)) NEW_VERSION="$MAJOR.$MINOR.$NEW_PATCH" done echo "New version: $NEW_VERSION" echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT echo "component_changed=$COMPONENT_CHANGED" >> $GITHUB_OUTPUT # Update versions in package.json files if [ "$BACKEND_UPDATE" = true ]; then echo "Updating backend version to $NEW_VERSION" cd backend && npm version $NEW_VERSION --no-git-tag-version cd .. fi if [ "$FRONTEND_UPDATE" = true ]; then echo "Updating frontend version to $NEW_VERSION" cd frontend && npm version $NEW_VERSION --no-git-tag-version cd .. fi # Check if there are changes to commit if [[ -n $(git status --porcelain) ]]; then echo "version_changed=true" >> $GITHUB_OUTPUT else echo "version_changed=false" >> $GITHUB_OUTPUT fi - name: Commit version bump if: steps.version.outputs.version_changed == 'true' run: | set -e # Exit on any error # First, ensure we have the latest changes echo "Fetching latest changes..." git fetch origin main # Check if we're behind and need to update LOCAL=$(git rev-parse HEAD) REMOTE=$(git rev-parse origin/main) if [ "$LOCAL" != "$REMOTE" ]; then echo "Local is behind remote, pulling changes..." git pull origin main --no-rebase fi COMPONENT="${{ steps.version.outputs.component_changed }}" if [ "$COMPONENT" = "both" ]; then git add backend/package.json backend/package-lock.json frontend/package.json frontend/package-lock.json git commit -m "chore: bump version to ${{ steps.version.outputs.new_version }} (backend + frontend)" elif [ "$COMPONENT" = "backend" ]; then git add backend/package.json backend/package-lock.json git commit -m "chore: bump backend version to ${{ steps.version.outputs.new_version }}" elif [ "$COMPONENT" = "frontend" ]; then git add frontend/package.json frontend/package-lock.json git commit -m "chore: bump frontend version to ${{ steps.version.outputs.new_version }}" fi # Pull latest changes before pushing to avoid conflicts echo "Pulling latest changes from origin/main..." if ! git pull --rebase origin main; then echo "Rebase failed, attempting to resolve..." # If rebase fails, abort and try a regular merge git rebase --abort || true git pull origin main --no-rebase fi # Push the changes with retry logic echo "Pushing version bump..." PUSH_SUCCESS=false for i in 1 2 3; do echo "Push attempt $i of 3..." # Try to push if git push origin main 2>&1; then echo "Successfully pushed version bump on attempt $i" PUSH_SUCCESS=true break else echo "Push failed on attempt $i" if [ $i -lt 3 ]; then echo "Waiting 5 seconds before retry..." sleep 5 echo "Pulling latest changes..." git fetch origin main # Try rebase first, fall back to merge if ! git rebase origin/main; then echo "Rebase failed, trying merge..." git rebase --abort 2>/dev/null || true git pull origin main --no-rebase fi fi fi done if [ "$PUSH_SUCCESS" = "false" ]; then echo "ERROR: Failed to push after 3 attempts" exit 1 fi - name: Create Git tag if: steps.version.outputs.version_changed == 'true' run: | COMPONENT="${{ steps.version.outputs.component_changed }}" if [ "$COMPONENT" = "both" ]; then TAG_MESSAGE="Release v${{ steps.version.outputs.new_version }} (backend + frontend)" elif [ "$COMPONENT" = "backend" ]; then TAG_MESSAGE="Release v${{ steps.version.outputs.new_version }} (backend)" elif [ "$COMPONENT" = "frontend" ]; then TAG_MESSAGE="Release v${{ steps.version.outputs.new_version }} (frontend)" fi git tag -a "v${{ steps.version.outputs.new_version }}" -m "$TAG_MESSAGE" git push origin "v${{ steps.version.outputs.new_version }}" trigger-drone: needs: version-bump if: needs.version-bump.outputs.version_changed == 'true' runs-on: ubuntu-latest steps: - name: Trigger Drone Build run: | echo "Version bumped to ${{ needs.version-bump.outputs.new_version }}" echo "Component(s) changed: ${{ needs.version-bump.outputs.component_changed }}" echo "Drone will automatically trigger on the new tag" # Drone CI will automatically trigger on the tag push event