Skip to content

docs(deployment): add migration instructions for Vercel deployment #157

docs(deployment): add migration instructions for Vercel deployment

docs(deployment): add migration instructions for Vercel deployment #157

Workflow file for this run

name: Auto Bump Version, Build & Publish
on:
push:
branches:
- main
jobs:
bump:
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
env:
MY_DASHBOARD_DATABASE_POSTGRES_URL: "postgres://postgres:pgtoolspassword@localhost:5432/postgres?schema=public"
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 24.11.1
# 🔍 Проверяем наличие [need ci]
- name: Check if [need ci] present
id: need_ci
run: |
if git log ${{ github.event.before }}..${{ github.sha }} --pretty=format:"%s%b" | grep -q "\[need ci\]"; then
echo "force_run=true" >> $GITHUB_OUTPUT
echo "✅ Found [need ci] — forcing full pipeline"
else
echo "force_run=false" >> $GITHUB_OUTPUT
echo "ℹ️ No [need ci] found"
fi
# 🔍 Определяем изменённые проекты
- name: Detect changed projects
id: detect
run: |
set +e # Disable immediate exit on error for better control
echo "Starting project detection..." || true
PROJECTS=$(find . -maxdepth 1 -type d ! -path "." ! -path "./.github*" ! -path "./.vscode*" ! -path "./scripts*" ! -path "./demo*" | while read d; do
if [ -f "$d/package.json" ]; then echo "$(basename "$d")"; fi
done) || true
echo "Found projects: '$PROJECTS'" || true
# Handle case where no projects are found
if [ -z "$PROJECTS" ]; then
echo "No projects found with package.json files" || true
PROJECTS=""
fi
if [ "${{ steps.need_ci.outputs.force_run }}" = "true" ]; then
CHANGED_PROJECTS="$PROJECTS"
echo "Force run enabled, using all projects" || true
else
echo "Checking for changed files..." || true
CHANGED=$(git diff --name-only ${{ github.event.before }} ${{ github.sha }} | cut -d/ -f1 | sort | uniq | grep -v -E '^(.github|.vscode|scripts|demo)$') || true
echo "Changed directories: '$CHANGED'" || true
if [ -n "$CHANGED" ] && [ -n "$PROJECTS" ]; then
echo "Filtering projects based on changes..." || true
echo "Projects list: $PROJECTS" || true
echo "Changed directories: $CHANGED" || true
# Use a more robust approach to avoid grep failures
CHANGED_PROJECTS=""
for project in $PROJECTS; do
for change in $CHANGED; do
if [ "$project" = "$change" ]; then
if [ -z "$CHANGED_PROJECTS" ]; then
CHANGED_PROJECTS="$project"
else
CHANGED_PROJECTS="$CHANGED_PROJECTS $project"
fi
echo "Matched project: $project" || true
break
fi
done
done
echo "Matched projects: '$CHANGED_PROJECTS'" || true
else
echo "No changes or no projects found, setting empty" || true
CHANGED_PROJECTS=""
fi
fi
echo "Changed projects: $CHANGED_PROJECTS" || true
CHANGED_PROJECTS_CLEAN=$(echo "$CHANGED_PROJECTS" | tr '\n' ' ' | sed 's/ $//' | sed 's/^[[:space:]]*//') || true
echo "Changed projects clean: '$CHANGED_PROJECTS_CLEAN'" || true
# Handle empty case
if [ -z "$CHANGED_PROJECTS_CLEAN" ]; then
echo "No changed projects to process" || true
CHANGED_PROJECTS_CLEAN=""
fi
# Use printf instead of echo -n for better compatibility
if command -v base64 >/dev/null 2>&1; then
ENCODED=$(printf "%s" "$CHANGED_PROJECTS_CLEAN" | base64 | tr -d '\n') || true
echo "Used base64 command for encoding" || true
else
# Fallback: just use the raw value
ENCODED=$(printf "%s" "$CHANGED_PROJECTS_CLEAN") || true
echo "Warning: base64 command not found, using raw value" || true
fi
# Make sure the GitHub output file exists and is writable
if [ -n "$GITHUB_OUTPUT" ] && [ -w "$GITHUB_OUTPUT" ]; then
echo "changed_projects_base64=$ENCODED" >> $GITHUB_OUTPUT || true
echo "Successfully wrote to GitHub output file" || true
else
echo "Warning: Cannot write to GitHub output file or GITHUB_OUTPUT not set" || true
echo "GITHUB_OUTPUT value: '$GITHUB_OUTPUT'" || true
echo "changed_projects_base64=$ENCODED" || true
fi
echo "Encoded value: $ENCODED" || true
# Debug the decoded value to ensure it works
if [ -n "$ENCODED" ] && command -v base64 >/dev/null 2>&1; then
DECODED=$(printf "%s" "$ENCODED" | base64 --decode 2>/dev/null || echo "") || true
echo "Decoded value: '$DECODED'" || true
else
echo "Empty encoded value or base64 not available" || true
fi
echo "Project detection completed successfully" || true
echo "Final output written to GitHub output" || true
exit 0 # Explicitly exit with success
# 🧩 Определяем тип bump'а
- name: Determine bump type
id: bump_type
run: |
MESSAGES=$(git log ${{ github.event.before }}..${{ github.sha }} --pretty=format:"%s%n%b")
if echo "$MESSAGES" | grep -qi "BREAKING CHANGE"; then
BUMP=major
elif echo "$MESSAGES" | grep -Eqi "^feat"; then
BUMP=minor
elif echo "$MESSAGES" | grep -Eqi "^fix"; then
BUMP=patch
else
BUMP=none
fi
echo "bump=$BUMP" >> $GITHUB_OUTPUT
echo "Detected bump type: $BUMP"
# 🚀 Bump, Build & Docker
- name: Bump, Build & Docker
if: steps.need_ci.outputs.force_run == 'true' || (steps.bump_type.outputs.bump != 'none' && steps.detect.outputs.changed_projects_base64 != '')
id: bump_versions
env:
DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
run: |
sudo apt-get install -y jq
npm install -g conventional-changelog-cli
if [ -n "${DOCKERHUB_USER}" ] && [ -n "${DOCKERHUB_TOKEN}" ]; then
echo "${DOCKERHUB_TOKEN}" | docker login -u "${DOCKERHUB_USER}" --password-stdin
else
echo "⚠️ DockerHub credentials missing — skipping Docker publish"
fi
BUMP=${{ steps.bump_type.outputs.bump }}
CHANGED_PROJECTS=$(echo "${{ steps.detect.outputs.changed_projects_base64 }}" | base64 --decode)
if [ -z "$CHANGED_PROJECTS" ]; then
echo "ℹ️ No changed projects detected. Exiting."
exit 0
fi
TAGS=""
cd web && npm ci && cd ..
for project in $CHANGED_PROJECTS; do
if [ -f "$project/package.json" ]; then
cd $project
NAME=$(jq -r '.name' package.json)
DESC=$(jq -r '.description // "No description"' package.json)
OLD_VERSION=$(jq -r '.version' package.json)
if [ "$BUMP" != "none" ]; then
npm version $BUMP --no-git-tag-version
fi
NEW_VERSION=$(jq -r '.version' package.json)
conventional-changelog -p angular -i CHANGELOG.md -s -r 1 || true
if jq -e '.scripts.build' package.json >/dev/null; then
npm ci
npm run build || echo "⚠️ Build failed for $project"
fi
if [ -f "Dockerfile" ] && [ -n "${DOCKERHUB_USER}" ]; then
IMAGE_NAME="${DOCKERHUB_USER}/${NAME}"
docker build -t "$IMAGE_NAME:$NEW_VERSION" -t "$IMAGE_NAME:latest" \
--label "org.opencontainers.image.title=$NAME" \
--label "org.opencontainers.image.description=$DESC" \
--label "org.opencontainers.image.version=$NEW_VERSION" \
--label "org.opencontainers.image.source=https://github.com/${{ github.repository }}" \
. || true
docker push "$IMAGE_NAME:$NEW_VERSION" || true
docker push "$IMAGE_NAME:latest" || true
fi
if [ -f "capacitor.config.ts" ]; then
echo "${{ secrets.KEYSTORE }}" | base64 --decode > "${{ github.workspace }}/$project/my-dashboard.jks"
docker run --rm \
-e KEYSTORE_PASSWORD="${{ secrets.KEYSTORE_PASSWORD }}" \
-e KEYSTORE_ALIAS="${{ secrets.KEYSTORE_ALIAS }}" \
-e KEYSTORE_ALIAS_PASSWORD="${{ secrets.KEYSTORE_ALIAS_PASSWORD }}" \
-e PRISMA_ENGINES_MIRROR="https://registry.npmmirror.com/-/binary/prisma" \
-v "${{ github.workspace }}/$project:/app" \
endykaufman/ionic-capacitor:latest
mkdir -p ${{ github.workspace }}/$project/artifacts
find ${{ github.workspace }}/$project/android/app/build/outputs/apk/release -type f -name "*.apk" -exec cp {} ${{ github.workspace }}/$project/artifacts/ \;
fi
cd -
TAG="${project}@${NEW_VERSION}"
git rev-parse "$TAG" >/dev/null 2>&1 || git tag "$TAG"
TAGS="$TAGS $TAG"
fi
done
echo "tags=$TAGS" >> $GITHUB_OUTPUT
# 💾 Commit and push changes
- name: Commit and push changes
if: steps.need_ci.outputs.force_run == 'true' || (steps.bump_type.outputs.bump != 'none' && steps.detect.outputs.changed_projects_base64 != '')
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add */package.json */CHANGELOG.md || true
git commit -m "chore: auto bump version [skip ci]" || echo "No changes"
git push origin main
git push origin --tags
# 📦 Upload artifacts
- name: Upload artifacts
if: steps.need_ci.outputs.force_run == 'true' || steps.bump_versions.outputs.tags != ''
uses: actions/upload-artifact@v4
with:
name: project-builds
path: "**/artifacts/**"
if-no-files-found: ignore
- name: Create releases & send Telegram message
if: steps.need_ci.outputs.force_run == 'true' || steps.bump_versions.outputs.tags != ''
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USERNAME }}
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
run: |
# --- функция для экранирования Markdown-спецсимволов
escape_markdown() {
local text="$1"
text="${text//_/\\_}"
text="${text//\*/\\*}"
text="${text//~/\\~}"
echo "$text"
}
# --- проверка [hidden]
if git log ${{ github.event.before }}..${{ github.sha }} --pretty=format:"%s%b" | grep -q "\[hidden\]"; then
SKIP_TELEGRAM=1
else
SKIP_TELEGRAM=0
fi
TAGS="${{ steps.bump_versions.outputs.tags }}"
if [ -z "$TAGS" ] && [ "${{ steps.need_ci.outputs.force_run }}" = "true" ]; then
TAGS=$(find . -maxdepth 1 -type d ! -path "." ! -path "./.github*" ! -path "./.vscode*" ! -path "./scripts*" ! -path "./demo*" -exec bash -c 'cd "{}" && if [ -f package.json ]; then NAME=$(jq -r .name package.json); VER=$(jq -r .version package.json); echo "$(basename "$PWD")@$VER"; fi' \;)
fi
for TAG in $TAGS; do
PROJECT=$(echo "$TAG" | cut -d@ -f1)
PKG="$PROJECT/package.json"
[ ! -f "$PKG" ] && continue
NAME=$(jq -r '.name' "$PKG")
VERSION=$(jq -r '.version' "$PKG")
DESC=$(jq -r '.description // "No description"' "$PKG")
CHANGELOG_PATH="$PROJECT/CHANGELOG.md"
ARTIFACTS_PATH="$PROJECT/artifacts"
RELEASE_URL="https://github.com/${{ github.repository }}/releases/tag/${TAG}"
PROJECT_URL="https://github.com/${{ github.repository }}/tree/main/${PROJECT}"
PIPELINE_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
# --- GitHub release через файл с переносами
if [ -f "$CHANGELOG_PATH" ]; then
BODY=$(git log --pretty=format:"• %s" ${{ github.event.before }}..${{ github.sha }} -- "$PROJECT")
[ -z "$BODY" ] && BODY=$(git log -n 5 --pretty=format:"• %s")
else
BODY=$(git log --pretty=format:"• %s" ${{ github.event.before }}..${{ github.sha }} -- "$PROJECT")
fi
NOTES="🚀 ${NAME} v${VERSION} released!
Changelog:
${BODY}
"
echo "$NOTES" > release_notes.txt
echo "ARTIFACTS_PATH: $ARTIFACTS_PATH"
if [ -d "$ARTIFACTS_PATH" ]; then
FILES=$(find "$ARTIFACTS_PATH" -type f)
echo "FILES: $FILES"
gh release create "$TAG" --title "$TAG" --notes-file release_notes.txt $FILES || echo "Release exists"
else
gh release create "$TAG" --title "$TAG" --notes-file release_notes.txt || echo "Release exists"
fi
# --- Telegram
if [ "$SKIP_TELEGRAM" -eq 0 ]; then
TG_BODY=$(echo -e "🚀 ${NAME} v${VERSION} released!%0A%0A_${DESC}_%0A%0A")
if [ -f "$CHANGELOG_PATH" ]; then
BODY=$(git log --pretty=format:"• %s" ${{ github.event.before }}..${{ github.sha }} -- "$PROJECT")
[ -z "$BODY" ] && BODY=$(git log -n 5 --pretty=format:"• %s")
# Заменяем переносы на %0A
NEW_TG_NOTES=$(echo "$BODY" | sed ':a;N;$!ba;s/\n/%0A/g')
# Экранирование спецсимволов
NEW_TG_NOTES=$(escape_markdown "$NEW_TG_NOTES")
TG_BODY+="*Changelog:*%0A${NEW_TG_NOTES}%0A"
else
BODY=$(git log --pretty=format:"• %s" ${{ github.event.before }}..${{ github.sha }} -- "$PROJECT")
# Заменяем переносы на %0A
NEW_TG_NOTES=$(echo "$BODY" | sed ':a;N;$!ba;s/\n/%0A/g')
# Экранирование спецсимволов
NEW_TG_NOTES=$(escape_markdown "$NEW_TG_NOTES")
TG_BODY+="*Recent commits:*%0A${NEW_TG_NOTES}%0A"
fi
BUTTONS="["
if [ -d "$ARTIFACTS_PATH" ]; then
while IFS= read -r FILE; do
EXT="${FILE##*.}"
if [[ "$EXT" =~ ^(apk|exe|zip|html)$ ]]; then
BASENAME=$(basename "$FILE")
SAFE_TAG=$(echo "$TAG" | sed 's/@/%40/g') # <-- заменяем @ на %40
FILE_URL="https://github.com/${{ github.repository }}/releases/download/${SAFE_TAG}/${BASENAME}"
BUTTONS="${BUTTONS}[{\"text\":\"💾 ${BASENAME}\",\"url\":\"${FILE_URL}\"}],"
fi
done < <(find "$ARTIFACTS_PATH" -type f)
fi
if [ -f "$PROJECT/Dockerfile" ]; then
BUTTONS="${BUTTONS}[{\"text\":\"🐳 Docker image\",\"url\":\"https://hub.docker.com/r/${DOCKERHUB_USER}/${NAME}\"}],"
fi
HOMEPAGE=$(jq -r '.homepage // empty' "$PKG")
if [ -n "$HOMEPAGE" ]; then
BUTTONS="${BUTTONS}[{\"text\":\"🌐 Homepage\",\"url\":\"${HOMEPAGE}\"}],"
fi
BUTTONS="${BUTTONS}[{\"text\":\"📂 Project folder\",\"url\":\"${PROJECT_URL}\"}],"
BUTTONS="${BUTTONS}[{\"text\":\"ℹ️ View release\",\"url\":\"${RELEASE_URL}\"}]"
BUTTONS="${BUTTONS}]"
BUTTONS=$(echo "$BUTTONS" | sed 's/,]/]/')
echo "TG_BODY: $TG_BODY"
echo "BUTTONS: $BUTTONS"
# Отправка Telegram с обработкой ошибок
if ! curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
-d "chat_id=${{ vars.TELEGRAM_CHAT_ID }}" \
-d "message_thread_id=${{ vars.TELEGRAM_CHAT_THREAD_ID }}" \
-d "parse_mode=Markdown" \
-d "disable_web_page_preview=true" \
-d "text=${TG_BODY}" \
-d "reply_markup={\"inline_keyboard\":${BUTTONS}}"; then
echo "⚠️ Telegram send failed!"
echo "TG_BODY: $TG_BODY"
echo "BUTTONS: $BUTTONS"
fi
fi
done