From dbbf54287f93c86e9cca064e4445b2966097255a Mon Sep 17 00:00:00 2001 From: Achintha Isuru Date: Thu, 5 Mar 2026 12:40:13 -0500 Subject: [PATCH] Add GitHub workflows, release scripts, PR template Add CI/CD and release automation assets: new GitHub Actions workflows (backend-foundation, hotfix-branch-creation, mobile-ci, product-release, web-quality), shell scripts for version/tag/release-note extraction and release-summary generation (.github/scripts/*), and a Pull Request template. Implements hotfix branch creation from tags, automatic tag name generation, version extraction from pubspec.yaml, CHANGELOG-based release notes extraction, selective mobile CI (detects changed files, builds and lints only affected Dart files), backend service test dry-runs, and automated GitHub release creation with summaries. --- .github/PULL_REQUEST_TEMPLATE.md | 101 ++++++ .github/scripts/create-release-summary.sh | 73 ++++ .github/scripts/extract-release-notes.sh | 65 ++++ .github/scripts/extract-version.sh | 48 +++ .github/scripts/generate-tag-name.sh | 22 ++ .github/workflows/backend-foundation.yml | 64 ++++ .github/workflows/hotfix-branch-creation.yml | 331 +++++++++++++++++++ .github/workflows/mobile-ci.yml | 248 ++++++++++++++ .github/workflows/product-release.yml | 145 ++++++++ .github/workflows/web-quality.yml | 59 ++++ 10 files changed, 1156 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100755 .github/scripts/create-release-summary.sh create mode 100755 .github/scripts/extract-release-notes.sh create mode 100755 .github/scripts/extract-version.sh create mode 100755 .github/scripts/generate-tag-name.sh create mode 100644 .github/workflows/backend-foundation.yml create mode 100644 .github/workflows/hotfix-branch-creation.yml create mode 100644 .github/workflows/mobile-ci.yml create mode 100644 .github/workflows/product-release.yml create mode 100644 .github/workflows/web-quality.yml diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..c7a2d1c5 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,101 @@ +## ๐Ÿ“‹ Description + + + + +--- + +## ๐ŸŽฏ Type of Change + + + +- [ ] ๐Ÿ› **Bug fix** (non-breaking change that fixes an issue) +- [ ] โœจ **Feature** (non-breaking change that adds functionality) +- [ ] ๐Ÿ“ **Documentation** (changes to docs, comments, or README) +- [ ] ๐Ÿ”ง **Refactor** (code change that doesn't affect functionality) +- [ ] โšก **Performance** (improvement in performance or optimization) +- [ ] ๐Ÿ” **Security** (security fix or improvement) +- [ ] ๐ŸŽจ **Style** (formatting, linting, or minor code style changes) +- [ ] ๐Ÿ—๏ธ **Architecture** (significant structural changes) + +--- + +## ๐Ÿ“ฆ Affected Areas + + + +- [ ] ๐Ÿ“ฑ **Mobile** (Flutter - Client/Worker app) +- [ ] ๐ŸŒ **Web** (React Dashboard) +- [ ] ๐Ÿ”Œ **Backend** (APIs, Data Connect, Cloud Functions) +- [ ] ๐Ÿ—„๏ธ **Database** (Schema changes, migrations) +- [ ] ๐Ÿš€ **CI/CD** (GitHub Actions, deployment configs) +- [ ] ๐Ÿ“š **Documentation** (Docs, onboarding guides) + +--- + +## ๐Ÿ”— Related Issues + + + +Closes # +Related to # + +--- + +## โœ… Testing + + + +**Test Details:** + + + +--- + +## ๐Ÿ”„ Breaking Changes + + + +- [ ] No breaking changes +- [ ] Yes, breaking changes: + +**Details:** + + + +--- + +## ๐ŸŽฏ Checklist + + + +- [ ] Code follows project style guidelines +- [ ] Self-review completed +- [ ] Comments added for complex logic +- [ ] Documentation updated (if applicable) +- [ ] No new console warnings/errors +- [ ] Tests pass locally +- [ ] Branch is up-to-date with `dev` +- [ ] Commit messages are clear and descriptive +- [ ] Sensitive data is not committed +- [ ] Environment variables documented (if added) + +--- + +## ๐Ÿ“ Additional Notes + + + + +--- + +## ๐Ÿ” Review Checklist for Maintainers + +- [ ] Code quality and readability +- [ ] Design patterns follow project conventions +- [ ] Test coverage is adequate +- [ ] Performance implications reviewed +- [ ] Security concerns addressed +- [ ] Documentation is complete +- [ ] Breaking changes properly communicated +- [ ] Cross-platform compatibility (if applicable) diff --git a/.github/scripts/create-release-summary.sh b/.github/scripts/create-release-summary.sh new file mode 100755 index 00000000..ddefb1d9 --- /dev/null +++ b/.github/scripts/create-release-summary.sh @@ -0,0 +1,73 @@ +#!/bin/bash +# Generate release summary for GitHub Actions +# Usage: ./create-release-summary.sh + +set -e + +APP=$1 +ENV=$2 +VERSION=$3 +TAG_NAME=$4 + +if [ -z "$APP" ] || [ -z "$ENV" ] || [ -z "$VERSION" ] || [ -z "$TAG_NAME" ]; then + echo "โŒ Error: Missing required parameters" + echo "Usage: ./create-release-summary.sh " + exit 1 +fi + +# Determine display names +if [ "$APP" = "worker-mobile-app" ]; then + APP_DISPLAY="Worker Product" + APP_EMOJI="๐Ÿ‘ท" +else + APP_DISPLAY="Client Product" + APP_EMOJI="๐Ÿ’ผ" +fi + +ENV_UPPER=$(echo "$ENV" | tr '[:lower:]' '[:upper:]') +RELEASE_NAME="Krow With Us - ${APP_DISPLAY} - ${ENV_UPPER} - v${VERSION}" + +# Environment emoji +case "$ENV" in + dev) + ENV_EMOJI="๐Ÿ”ง" + ;; + stage) + ENV_EMOJI="๐ŸŽญ" + ;; + prod) + ENV_EMOJI="๐Ÿš€" + ;; + *) + ENV_EMOJI="๐Ÿ“ฆ" + ;; +esac + +# Generate summary +cat << EOF >> $GITHUB_STEP_SUMMARY +## ๐ŸŽ‰ Release Created Successfully + +### ${APP_EMOJI} Application Details +- **App:** ${APP_DISPLAY} +- **Environment:** ${ENV_EMOJI} ${ENV_UPPER} +- **Version:** \`${VERSION}\` +- **Tag:** \`${TAG_NAME}\` + +### ๐Ÿ“ฆ Release Information +**Release Name:** ${RELEASE_NAME} + +### โœ… Next Steps + +1. ๐Ÿ” **Verify** the tag and release on GitHub +2. ๐Ÿ—๏ธ **Trigger** CodeMagic build (if configured) +3. ๐Ÿ“ฑ **Monitor** app store deployment +4. ๐Ÿ“š **Update** project documentation if needed +5. ๐ŸŽฏ **Communicate** release to stakeholders + +### ๐Ÿ”— Quick Links +- [View Tag](../../releases/tag/${TAG_NAME}) +- [Release Documentation](../../docs/release/MOBILE_RELEASE_PLAN.md) +- [CHANGELOG](../../apps/mobile/apps/${APP}/CHANGELOG.md) +EOF + +echo "โœ… Summary generated successfully" diff --git a/.github/scripts/extract-release-notes.sh b/.github/scripts/extract-release-notes.sh new file mode 100755 index 00000000..f29530fe --- /dev/null +++ b/.github/scripts/extract-release-notes.sh @@ -0,0 +1,65 @@ +#!/bin/bash +# Extract release notes from CHANGELOG for a specific version +# Usage: ./extract-release-notes.sh + +set -e + +APP=$1 +VERSION=$2 +ENV=$3 +TAG_NAME=$4 +OUTPUT_FILE=$5 + +if [ -z "$APP" ] || [ -z "$VERSION" ] || [ -z "$ENV" ] || [ -z "$TAG_NAME" ] || [ -z "$OUTPUT_FILE" ]; then + echo "โŒ Error: Missing required parameters" + echo "Usage: ./extract-release-notes.sh " + exit 1 +fi + +# Determine CHANGELOG path and app name +if [ "$APP" = "worker-mobile-app" ]; then + CHANGELOG_PATH="apps/mobile/apps/staff/CHANGELOG.md" + APP_NAME="Staff Product (Worker)" +else + CHANGELOG_PATH="apps/mobile/apps/client/CHANGELOG.md" + APP_NAME="Client Product" +fi + +# Try to extract release notes for this version +if [ -f "$CHANGELOG_PATH" ]; then + echo "๐Ÿ“ Found CHANGELOG at $CHANGELOG_PATH" + + # Extract section for this version + # Look for ## [VERSION] and collect until next ## [ or end of file + NOTES=$(awk "/## \[${VERSION}\]/,/^## \[/" "$CHANGELOG_PATH" | sed '1d;$d' | sed '/^$/d') + + if [ -z "$NOTES" ]; then + echo "โš ๏ธ Warning: No CHANGELOG entry found for version $VERSION" + NOTES="Release $VERSION for $APP_NAME + +โš ๏ธ No CHANGELOG entry found for this version. Please update the CHANGELOG manually. + +**Environment:** $ENV +**Tag:** $TAG_NAME" + else + echo "โœ… Extracted release notes for version $VERSION" + NOTES="# $APP_NAME - Release $VERSION + +$NOTES + +--- + +**Environment:** $ENV +**Tag:** $TAG_NAME" + fi +else + echo "โš ๏ธ Warning: CHANGELOG not found at $CHANGELOG_PATH" + NOTES="Release $VERSION for $APP_NAME + +**Environment:** $ENV +**Tag:** $TAG_NAME" +fi + +# Save to output file +echo "$NOTES" > "$OUTPUT_FILE" +echo "โœ… Release notes saved to $OUTPUT_FILE" diff --git a/.github/scripts/extract-version.sh b/.github/scripts/extract-version.sh new file mode 100755 index 00000000..88d97dd8 --- /dev/null +++ b/.github/scripts/extract-version.sh @@ -0,0 +1,48 @@ +#!/bin/bash +# Extract version from version file for products +# Usage: ./extract-version.sh +# app: worker-mobile-app or client-mobile-app + +set -e + +APP=$1 + +if [ -z "$APP" ]; then + echo "โŒ Error: App parameter required (worker-mobile-app or client-mobile-app)" + exit 1 +fi + +# Determine pubspec path +if [ "$APP" = "worker-mobile-app" ]; then + PUBSPEC_PATH="apps/mobile/apps/staff/pubspec.yaml" + APP_NAME="Staff Product (Worker)" +else + PUBSPEC_PATH="apps/mobile/apps/client/pubspec.yaml" + APP_NAME="Client Product" +fi + +# Check if pubspec exists +if [ ! -f "$PUBSPEC_PATH" ]; then + echo "โŒ Error: pubspec.yaml not found at $PUBSPEC_PATH" + exit 1 +fi + +# Extract version (format: X.Y.Z+buildNumber) +VERSION_LINE=$(grep "^version:" "$PUBSPEC_PATH") +if [ -z "$VERSION_LINE" ]; then + echo "โŒ Error: Could not find version in $PUBSPEC_PATH" + exit 1 +fi + +# Extract just the semantic version (before the +) +VERSION=$(echo "$VERSION_LINE" | sed 's/version: *//' | sed 's/+.*//' | tr -d ' ') + +# Validate version format +if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "โŒ Error: Invalid version format in pubspec.yaml: $VERSION" + echo "Expected format: X.Y.Z (e.g., 0.1.0)" + exit 1 +fi + +echo "โœ… Extracted version from $PUBSPEC_PATH: $VERSION" +echo "$VERSION" diff --git a/.github/scripts/generate-tag-name.sh b/.github/scripts/generate-tag-name.sh new file mode 100755 index 00000000..c779b542 --- /dev/null +++ b/.github/scripts/generate-tag-name.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# Generate tag name for product release +# Usage: ./generate-tag-name.sh + +set -e + +APP=$1 +ENV=$2 +VERSION=$3 + +if [ -z "$APP" ] || [ -z "$ENV" ] || [ -z "$VERSION" ]; then + echo "โŒ Error: Missing required parameters" + echo "Usage: ./generate-tag-name.sh " + exit 1 +fi + +# Strip -mobile-app suffix from app name for cleaner tag names +# worker-mobile-app -> worker, client-mobile-app -> client +APP_TAG=$(echo "$APP" | sed 's/-mobile-app$//') + +TAG_NAME="krow-withus-${APP_TAG}-mobile/${ENV}-v${VERSION}" +echo "$TAG_NAME" diff --git a/.github/workflows/backend-foundation.yml b/.github/workflows/backend-foundation.yml new file mode 100644 index 00000000..0e408f8f --- /dev/null +++ b/.github/workflows/backend-foundation.yml @@ -0,0 +1,64 @@ +name: Backend Foundation + +on: + pull_request: + branches: + - dev + - main + push: + branches: + - dev + - main + +jobs: + backend-foundation-makefile: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Validate backend make targets + run: | + make backend-help + make help | grep "backend-" + + - name: Dry-run backend automation targets + run: | + make -n backend-enable-apis ENV=dev + make -n backend-bootstrap-dev ENV=dev + make -n backend-deploy-core ENV=dev + make -n backend-deploy-commands ENV=dev + make -n backend-deploy-workers ENV=dev + make -n backend-smoke-core ENV=dev + make -n backend-smoke-commands ENV=dev + make -n backend-logs-core ENV=dev + + backend-services-tests: + runs-on: ubuntu-latest + strategy: + matrix: + service: + - backend/core-api + - backend/command-api + defaults: + run: + working-directory: ${{ matrix.service }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + cache-dependency-path: ${{ matrix.service }}/package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Run tests + env: + AUTH_BYPASS: "true" + LLM_MOCK: "true" + run: npm test diff --git a/.github/workflows/hotfix-branch-creation.yml b/.github/workflows/hotfix-branch-creation.yml new file mode 100644 index 00000000..2cb77a7a --- /dev/null +++ b/.github/workflows/hotfix-branch-creation.yml @@ -0,0 +1,331 @@ +name: ๐Ÿšจ Hotfix Branch Creation + +on: + workflow_dispatch: + inputs: + app: + description: '๐Ÿ“ฆ Product' + required: true + type: choice + options: + - worker-mobile-app + - client-mobile-app + tag: + description: '๐Ÿท๏ธ Current Tag (e.g., krow-withus-worker-mobile/prod-v0.1.0 or dev/stage)' + required: true + type: string + issue_description: + description: '๐Ÿ“ Brief issue description' + required: true + type: string + +jobs: + create-hotfix-branch: + name: ๐Ÿšจ Create Hotfix Branch + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + + steps: + - name: ๐Ÿ“ฅ Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: ๐Ÿ” Validate tag exists + id: validate_tag + run: | + TAG="${{ github.event.inputs.tag }}" + + if ! git rev-parse "$TAG" >/dev/null 2>&1; then + echo "โŒ Error: Tag '$TAG' does not exist" + echo "Available tags:" + git tag -l "krow-withus-*-mobile/*" | tail -20 + exit 1 + fi + + echo "โœ… Tag exists: $TAG" + + # Extract version from tag + VERSION=$(echo "$TAG" | grep -oP 'v\K[0-9]+\.[0-9]+\.[0-9]+' || echo "") + if [ -z "$VERSION" ]; then + echo "โŒ Error: Could not extract version from tag" + exit 1 + fi + + echo "current_version=${VERSION}" >> $GITHUB_OUTPUT + echo "๐Ÿ“Œ Current version: $VERSION" + + - name: ๐Ÿ”ข Calculate hotfix version + id: hotfix_version + run: | + CURRENT="${{ steps.validate_tag.outputs.current_version }}" + + # Split version into parts + IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT" + + # Increment PATCH version + NEW_PATCH=$((PATCH + 1)) + HOTFIX_VERSION="${MAJOR}.${MINOR}.${NEW_PATCH}" + + echo "hotfix_version=${HOTFIX_VERSION}" >> $GITHUB_OUTPUT + echo "๐Ÿ†• Hotfix version: $HOTFIX_VERSION" + + - name: ๐ŸŒฟ Generate branch name + id: branch + run: | + APP="${{ github.event.inputs.app }}" + VERSION="${{ steps.hotfix_version.outputs.hotfix_version }}" + + # Strip -mobile-app suffix for cleaner branch names + APP_CLEAN=$(echo "$APP" | sed 's/-mobile-app$//') + + BRANCH_NAME="hotfix/krow-withus-${APP_CLEAN}-mobile-v${VERSION}" + echo "branch_name=${BRANCH_NAME}" >> $GITHUB_OUTPUT + echo "๐ŸŒฟ Branch to create: $BRANCH_NAME" + + - name: ๐Ÿ” Check if hotfix branch already exists + run: | + BRANCH="${{ steps.branch.outputs.branch_name }}" + + if git ls-remote --heads origin "$BRANCH" | grep -q "$BRANCH"; then + echo "โŒ Error: Branch $BRANCH already exists" + exit 1 + fi + + echo "โœ… Branch does not exist, proceeding..." + + - name: ๐ŸŒฟ Create hotfix branch from tag + run: | + TAG="${{ github.event.inputs.tag }}" + BRANCH="${{ steps.branch.outputs.branch_name }}" + + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + # Checkout the tag + git checkout "$TAG" + + # Create new branch + git checkout -b "$BRANCH" + + echo "โœ… Created branch $BRANCH from tag $TAG" + + - name: ๐Ÿ“ Update version files + id: update_versions + run: | + APP="${{ github.event.inputs.app }}" + HOTFIX_VERSION="${{ steps.hotfix_version.outputs.hotfix_version }}" + + if [ "$APP" = "worker-mobile-app" ]; then + PUBSPEC_PATH="apps/mobile/apps/staff/pubspec.yaml" + CHANGELOG_PATH="apps/mobile/apps/staff/CHANGELOG.md" + APP_NAME="Staff Product" + else + PUBSPEC_PATH="apps/mobile/apps/client/pubspec.yaml" + CHANGELOG_PATH="apps/mobile/apps/client/CHANGELOG.md" + APP_NAME="Client Product" + fi + + # Update pubspec.yaml version + if [ -f "$PUBSPEC_PATH" ]; then + # Extract current version and build number + CURRENT_VERSION_LINE=$(grep "^version:" "$PUBSPEC_PATH") + CURRENT_BUILD=$(echo "$CURRENT_VERSION_LINE" | grep -oP '\+\K[0-9]+' || echo "1") + NEW_BUILD=$((CURRENT_BUILD + 1)) + + # Update version line + sed -i "s/^version:.*/version: ${HOTFIX_VERSION}+${NEW_BUILD}/" "$PUBSPEC_PATH" + + echo "โœ… Updated $PUBSPEC_PATH to ${HOTFIX_VERSION}+${NEW_BUILD}" + echo "updated_files=true" >> $GITHUB_OUTPUT + else + echo "โš ๏ธ Warning: $PUBSPEC_PATH not found" + echo "updated_files=false" >> $GITHUB_OUTPUT + fi + + - name: ๐Ÿ“‹ Add CHANGELOG entry + run: | + APP="${{ github.event.inputs.app }}" + HOTFIX_VERSION="${{ steps.hotfix_version.outputs.hotfix_version }}" + ISSUE="${{ github.event.inputs.issue_description }}" + + if [ "$APP" = "worker-mobile-app" ]; then + CHANGELOG_PATH="apps/mobile/apps/staff/CHANGELOG.md" + APP_NAME="Staff Product" + else + CHANGELOG_PATH="apps/mobile/apps/client/CHANGELOG.md" + APP_NAME="Client Product" + fi + + if [ -f "$CHANGELOG_PATH" ]; then + DATE=$(date +%Y-%m-%d) + + # Create hotfix entry + HOTFIX_ENTRY="## [${HOTFIX_VERSION}] - ${DATE} - HOTFIX + +### Fixed +- ${ISSUE} + +--- + +" + + # Insert after the first line (title) + sed -i "1 a\\ +\\ +$HOTFIX_ENTRY" "$CHANGELOG_PATH" + + echo "โœ… Added CHANGELOG entry for hotfix $HOTFIX_VERSION" + else + echo "โš ๏ธ Warning: $CHANGELOG_PATH not found" + fi + + - name: ๐Ÿ’พ Commit version changes + run: | + HOTFIX_VERSION="${{ steps.hotfix_version.outputs.hotfix_version }}" + ISSUE="${{ github.event.inputs.issue_description }}" + + git add -A + git commit -m "chore: prepare hotfix v${HOTFIX_VERSION} + +HOTFIX: ${ISSUE} + +- Bump version to ${HOTFIX_VERSION} +- Add CHANGELOG entry +- Ready for bug fix commits + +From tag: ${{ github.event.inputs.tag }}" + + echo "โœ… Committed version changes" + + - name: ๐Ÿš€ Push hotfix branch + run: | + BRANCH="${{ steps.branch.outputs.branch_name }}" + + git push origin "$BRANCH" + + echo "โœ… Pushed branch: $BRANCH" + + - name: ๐Ÿ“„ Create Pull Request + id: create_pr + env: + GH_TOKEN: ${{ github.token }} + run: | + BRANCH="${{ steps.branch.outputs.branch_name }}" + HOTFIX_VERSION="${{ steps.hotfix_version.outputs.hotfix_version }}" + ISSUE="${{ github.event.inputs.issue_description }}" + APP="${{ github.event.inputs.app }}" + + # Strip -mobile-app suffix for cleaner tag names + APP_CLEAN=$(echo "$APP" | sed 's/-mobile-app$//') + + if [ "$APP" = "worker-mobile-app" ]; then + APP_DISPLAY="Worker Product" + else + APP_DISPLAY="Client Product" + fi + + PR_TITLE="๐Ÿšจ HOTFIX: ${APP_DISPLAY} v${HOTFIX_VERSION} - ${ISSUE}" + + PR_BODY="## ๐Ÿšจ HOTFIX - URGENT FIX + +**App:** ${APP_DISPLAY} +**Version:** ${HOTFIX_VERSION} +**From:** \`${{ github.event.inputs.tag }}\` + +### Issue +${ISSUE} + +### Impact + + +### Solution + + +### Testing + + +--- + +## โš ๏ธ Hotfix Process + +1. โœ… Hotfix branch created +2. โณ **NEXT:** Make your bug fix commits to this branch +3. โณ Test the fix locally +4. โณ Request expedited review (< 15 minutes) +5. โณ Merge to main and create production tag + +### To add your fix: +\`\`\`bash +git checkout $BRANCH +# Make your changes +git commit -m \"fix: [description]\" +git push origin $BRANCH +\`\`\` + +### After merging: +\`\`\`bash +# Tag and release +git checkout main +git pull origin main +git tag -a krow-withus-${APP_CLEAN}-mobile/prod-v${HOTFIX_VERSION} -m \"HOTFIX: ${ISSUE}\" +git push origin krow-withus-${APP_CLEAN}-mobile/prod-v${HOTFIX_VERSION} +\`\`\` + +--- + +**Ref:** [Hotfix Process Documentation](../docs/release/HOTFIX_PROCESS.md)" + + # Create PR + PR_URL=$(gh pr create \ + --base main \ + --head "$BRANCH" \ + --title "$PR_TITLE" \ + --body "$PR_BODY" \ + --label "hotfix,urgent,production") + + echo "pr_url=${PR_URL}" >> $GITHUB_OUTPUT + echo "โœ… Pull Request created: $PR_URL" + + - name: ๐Ÿ“Š Hotfix Summary + run: | + # Strip -mobile-app suffix for cleaner tag names + APP_CLEAN=$(echo "${{ github.event.inputs.app }}" | sed 's/-mobile-app$//') + + echo "## ๐Ÿšจ Hotfix Branch Created" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**App:** ${{ github.event.inputs.app }}" >> $GITHUB_STEP_SUMMARY + echo "**Issue:** ${{ github.event.inputs.issue_description }}" >> $GITHUB_STEP_SUMMARY + echo "**From Tag:** \`${{ github.event.inputs.tag }}\`" >> $GITHUB_STEP_SUMMARY + echo "**Current Version:** ${{ steps.validate_tag.outputs.current_version }}" >> $GITHUB_STEP_SUMMARY + echo "**Hotfix Version:** ${{ steps.hotfix_version.outputs.hotfix_version }}" >> $GITHUB_STEP_SUMMARY + echo "**Branch:** \`${{ steps.branch.outputs.branch_name }}\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ๐Ÿ”ง Next Steps" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "1. **Checkout the hotfix branch:**" >> $GITHUB_STEP_SUMMARY + echo " \`\`\`bash" >> $GITHUB_STEP_SUMMARY + echo " git fetch origin" >> $GITHUB_STEP_SUMMARY + echo " git checkout ${{ steps.branch.outputs.branch_name }}" >> $GITHUB_STEP_SUMMARY + echo " \`\`\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "2. **Make your bug fix(es)** - Keep changes minimal!" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "3. **Test locally** - Verify the fix works" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "4. **Request expedited review** - Target < 15 minutes" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "5. **Merge PR and create production tag:**" >> $GITHUB_STEP_SUMMARY + echo " \`\`\`bash" >> $GITHUB_STEP_SUMMARY + echo " git checkout main" >> $GITHUB_STEP_SUMMARY + echo " git pull origin main" >> $GITHUB_STEP_SUMMARY + echo " git tag -a krow-withus-${APP_CLEAN}-mobile/prod-v${{ steps.hotfix_version.outputs.hotfix_version }} -m \"HOTFIX: ${{ github.event.inputs.issue_description }}\"" >> $GITHUB_STEP_SUMMARY + echo " git push origin krow-withus-${APP_CLEAN}-mobile/prod-v${{ steps.hotfix_version.outputs.hotfix_version }}" >> $GITHUB_STEP_SUMMARY + echo " \`\`\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + if [ -n "${{ steps.create_pr.outputs.pr_url }}" ]; then + echo "**Pull Request:** ${{ steps.create_pr.outputs.pr_url }}" >> $GITHUB_STEP_SUMMARY + fi diff --git a/.github/workflows/mobile-ci.yml b/.github/workflows/mobile-ci.yml new file mode 100644 index 00000000..1a439740 --- /dev/null +++ b/.github/workflows/mobile-ci.yml @@ -0,0 +1,248 @@ +name: Mobile CI + +on: + pull_request: + paths: + - 'apps/mobile/**' + - '.github/workflows/mobile-ci.yml' + push: + branches: + - main + paths: + - 'apps/mobile/**' + - '.github/workflows/mobile-ci.yml' + +jobs: + detect-changes: + name: ๐Ÿ” Detect Mobile Changes + runs-on: ubuntu-latest + outputs: + mobile-changed: ${{ steps.detect.outputs.mobile-changed }} + changed-files: ${{ steps.detect.outputs.changed-files }} + steps: + - name: ๐Ÿ“ฅ Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: ๐Ÿ”Ž Detect changes in apps/mobile + id: detect + run: | + if [[ "${{ github.event_name }}" == "pull_request" ]]; then + # For PR, compare all changes against base branch (not just latest commit) + # Using three-dot syntax (...) shows all files changed in the PR branch + BASE_REF="${{ github.event.pull_request.base.ref }}" + CHANGED_FILES=$(git diff --name-only origin/$BASE_REF...HEAD 2>/dev/null || echo "") + else + # For push, compare with previous commit + if [[ "${{ github.event.before }}" == "0000000000000000000000000000000000000000" ]]; then + # Initial commit, check all files + CHANGED_FILES=$(git ls-tree -r --name-only HEAD) + else + CHANGED_FILES=$(git diff --name-only ${{ github.event.before }} ${{ github.event.after }}) + fi + fi + + # Filter for files in apps/mobile + MOBILE_CHANGED=$(echo "$CHANGED_FILES" | grep -c "^apps/mobile/" || echo "0") + + if [[ $MOBILE_CHANGED -gt 0 ]]; then + echo "mobile-changed=true" >> $GITHUB_OUTPUT + # Get list of changed Dart files in apps/mobile + MOBILE_FILES=$(echo "$CHANGED_FILES" | grep "^apps/mobile/" | grep "\.dart$" || echo "") + echo "changed-files<> $GITHUB_OUTPUT + echo "$MOBILE_FILES" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + echo "โœ… Changes detected in apps/mobile/" + echo "๐Ÿ“ Changed files:" + echo "$MOBILE_FILES" + else + echo "mobile-changed=false" >> $GITHUB_OUTPUT + echo "changed-files=" >> $GITHUB_OUTPUT + echo "โญ๏ธ No changes detected in apps/mobile/ - skipping checks" + fi + + compile: + name: ๐Ÿ—๏ธ Compile Mobile App + runs-on: macos-latest + needs: detect-changes + if: needs.detect-changes.outputs.mobile-changed == 'true' + steps: + - name: ๐Ÿ“ฅ Checkout repository + uses: actions/checkout@v4 + + - name: ๐Ÿฆ‹ Set up Flutter + uses: subosito/flutter-action@v2 + with: + flutter-version: '3.38.x' + channel: 'stable' + cache: true + + - name: ๐Ÿ”ง Install Firebase CLI + run: | + npm install -g firebase-tools + + - name: ๐Ÿ“ฆ Get Flutter dependencies + run: | + make mobile-install + + - name: ๐Ÿ”จ Run compilation check + run: | + set -o pipefail + + echo "๐Ÿ—๏ธ Building client app for Android (dev mode)..." + if ! make mobile-client-build PLATFORM=apk MODE=debug 2>&1 | tee client_build.txt; then + echo "" + echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + echo "โŒ CLIENT APP BUILD FAILED" + echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + exit 1 + fi + + echo "" + echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + + echo "๐Ÿ—๏ธ Building staff app for Android (dev mode)..." + if ! make mobile-staff-build PLATFORM=apk MODE=debug 2>&1 | tee staff_build.txt; then + echo "" + echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + echo "โŒ STAFF APP BUILD FAILED" + echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + exit 1 + fi + + echo "" + echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + echo "โœ… Build check PASSED - Both apps built successfully" + echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + + lint: + name: ๐Ÿงน Lint Changed Files + runs-on: macos-latest + needs: detect-changes + if: needs.detect-changes.outputs.mobile-changed == 'true' && needs.detect-changes.outputs.changed-files != '' + steps: + - name: ๐Ÿ“ฅ Checkout repository + uses: actions/checkout@v4 + + - name: ๐Ÿฆ‹ Set up Flutter + uses: subosito/flutter-action@v2 + with: + flutter-version: '3.38.x' + channel: 'stable' + cache: true + + - name: ๐Ÿ”ง Install Firebase CLI + run: | + npm install -g firebase-tools + + - name: ๐Ÿ“ฆ Get Flutter dependencies + run: | + make mobile-install + + - name: ๐Ÿ” Lint changed Dart files + run: | + set -o pipefail + + # Get the list of changed files + CHANGED_FILES="${{ needs.detect-changes.outputs.changed-files }}" + + if [[ -z "$CHANGED_FILES" ]]; then + echo "โญ๏ธ No Dart files changed, skipping lint" + exit 0 + fi + + echo "๐ŸŽฏ Running lint on changed files:" + echo "$CHANGED_FILES" + echo "" + + # Run dart analyze on each changed file + HAS_ERRORS=false + FAILED_FILES=() + + while IFS= read -r file; do + if [[ -n "$file" && "$file" == *.dart && -f "$file" ]]; then + echo "๐Ÿ“ Analyzing: $file" + + if ! dart analyze "$file" 2>&1 | tee -a lint_output.txt; then + HAS_ERRORS=true + FAILED_FILES+=("$file") + fi + echo "" + fi + done <<< "$CHANGED_FILES" + + echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + + # Check if there were any errors + if [[ "$HAS_ERRORS" == "true" ]]; then + echo "โŒ LINT ERRORS FOUND IN ${#FAILED_FILES[@]} FILE(S):" + echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + for file in "${FAILED_FILES[@]}"; do + echo " โŒ $file" + done + echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + echo "" + echo "See details above for each file" + exit 1 + else + echo "โœ… Lint check PASSED for all changed files" + echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" + fi + + status-check: + name: ๐Ÿ“Š CI Status Check + runs-on: ubuntu-latest + needs: [detect-changes, compile, lint] + if: always() + steps: + - name: ๐Ÿ” Check mobile changes detected + run: | + if [[ "${{ needs.detect-changes.outputs.mobile-changed }}" == "true" ]]; then + echo "โœ… Mobile changes detected - running full checks" + else + echo "โญ๏ธ No mobile changes detected - skipping checks" + fi + + - name: ๐Ÿ—๏ธ Report compilation status + if: needs.detect-changes.outputs.mobile-changed == 'true' + run: | + if [[ "${{ needs.compile.result }}" == "success" ]]; then + echo "โœ… Compilation check: PASSED" + else + echo "โŒ Compilation check: FAILED" + exit 1 + fi + + - name: ๐Ÿงน Report lint status + if: needs.detect-changes.outputs.mobile-changed == 'true' && needs.detect-changes.outputs.changed-files != '' + run: | + if [[ "${{ needs.lint.result }}" == "success" ]]; then + echo "โœ… Lint check: PASSED" + else + echo "โŒ Lint check: FAILED" + exit 1 + fi + + - name: ๐ŸŽ‰ Final status + if: always() + run: | + echo "" + echo "โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—" + echo "โ•‘ ๐Ÿ“Š Mobile CI Pipeline Summary โ•‘" + echo "โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•" + echo "" + echo "๐Ÿ” Change Detection: ${{ needs.detect-changes.result }}" + echo "๐Ÿ—๏ธ Compilation: ${{ needs.compile.result }}" + echo "๐Ÿงน Lint Check: ${{ needs.lint.result }}" + echo "" + + if [[ "${{ needs.detect-changes.result }}" != "success" || \ + ("${{ needs.detect-changes.outputs.mobile-changed }}" == "true" && \ + ("${{ needs.compile.result }}" != "success" || "${{ needs.lint.result }}" != "success")) ]]; then + echo "โŒ Pipeline FAILED" + exit 1 + else + echo "โœ… Pipeline PASSED" + fi + diff --git a/.github/workflows/product-release.yml b/.github/workflows/product-release.yml new file mode 100644 index 00000000..966e405a --- /dev/null +++ b/.github/workflows/product-release.yml @@ -0,0 +1,145 @@ +name: ๐Ÿ“ฆ Product Release + +on: + workflow_dispatch: + inputs: + app: + description: '๐Ÿ“ฆ Product' + required: true + type: choice + options: + - worker-mobile-app + - client-mobile-app + environment: + description: '๐ŸŒ Environment' + required: true + type: choice + options: + - dev + - stage + - prod + create_github_release: + description: '๐Ÿ“ฆ Create GitHub Release' + required: true + type: boolean + default: true + prerelease: + description: '๐Ÿ”– Mark as Pre-release' + required: false + type: boolean + default: false + +jobs: + validate-and-create-release: + name: ๐Ÿš€ Create Product Release + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - name: ๐Ÿ“ฅ Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: ๏ฟฝ Make scripts executable + run: | + chmod +x .github/scripts/*.sh + echo "โœ… Scripts are now executable" + + - name: ๐Ÿ“– Extract version from version file + id: version + run: | + VERSION=$(.github/scripts/extract-version.sh "${{ github.event.inputs.app }}") + echo "version=${VERSION}" >> $GITHUB_OUTPUT + echo "๐Ÿ“Œ Extracted version: ${VERSION}" + + - name: ๐Ÿท๏ธ Generate tag name + id: tag + run: | + TAG_NAME=$(.github/scripts/generate-tag-name.sh \ + "${{ github.event.inputs.app }}" \ + "${{ github.event.inputs.environment }}" \ + "${{ steps.version.outputs.version }}") + echo "tag_name=${TAG_NAME}" >> $GITHUB_OUTPUT + echo "๐ŸŽฏ Tag to create: ${TAG_NAME}" + + - name: ๐Ÿ” Check if tag already exists + run: | + TAG_NAME="${{ steps.tag.outputs.tag_name }}" + if git rev-parse "$TAG_NAME" >/dev/null 2>&1; then + echo "โŒ Error: Tag $TAG_NAME already exists" + echo "๐Ÿ’ก Tip: Update the version in the version file before creating a new release" + exit 1 + fi + echo "โœ… Tag does not exist, proceeding..." + + - name: ๐Ÿ“‹ Extract release notes from CHANGELOG + id: release_notes + run: | + .github/scripts/extract-release-notes.sh \ + "${{ github.event.inputs.app }}" \ + "${{ steps.version.outputs.version }}" \ + "${{ github.event.inputs.environment }}" \ + "${{ steps.tag.outputs.tag_name }}" \ + "/tmp/release_notes.md" + echo "notes_file=/tmp/release_notes.md" >> $GITHUB_OUTPUT + + - name: ๐Ÿท๏ธ Create Git Tag + run: | + TAG_NAME="${{ steps.tag.outputs.tag_name }}" + APP="${{ github.event.inputs.app }}" + ENV="${{ github.event.inputs.environment }}" + VERSION="${{ steps.version.outputs.version }}" + + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + git tag -a "$TAG_NAME" -m "๐Ÿš€ Release ${APP} product ${VERSION} to ${ENV}" + git push origin "$TAG_NAME" + + echo "โœ… Tag created and pushed: $TAG_NAME" + + - name: ๐Ÿ“ฆ Create GitHub Release + if: ${{ github.event.inputs.create_github_release == 'true' }} + env: + GH_TOKEN: ${{ github.token }} + run: | + TAG_NAME="${{ steps.tag.outputs.tag_name }}" + APP="${{ github.event.inputs.app }}" + ENV="${{ github.event.inputs.environment }}" + VERSION="${{ steps.version.outputs.version }}" + + # Generate release title + if [ "$APP" = "worker-mobile-app" ]; then + APP_DISPLAY="Worker Mobile Application" + else + APP_DISPLAY="Client Mobile Application" + fi + + ENV_UPPER=$(echo "$ENV" | tr '[:lower:]' '[:upper:]') + RELEASE_NAME="Krow With Us - ${APP_DISPLAY} - ${ENV_UPPER} - v${VERSION}" + + echo "๐Ÿ“ฆ Creating GitHub Release: $RELEASE_NAME" + + # Create release + if [ "${{ github.event.inputs.prerelease }}" = "true" ]; then + gh release create "$TAG_NAME" \ + --title "$RELEASE_NAME" \ + --notes-file "${{ steps.release_notes.outputs.notes_file }}" \ + --prerelease + echo "๐Ÿ”– Pre-release created successfully" + else + gh release create "$TAG_NAME" \ + --title "$RELEASE_NAME" \ + --notes-file "${{ steps.release_notes.outputs.notes_file }}" + echo "โœ… Release created successfully" + fi + + - name: ๐Ÿ“Š Generate Release Summary + run: | + .github/scripts/create-release-summary.sh \ + "${{ github.event.inputs.app }}" \ + "${{ github.event.inputs.environment }}" \ + "${{ steps.version.outputs.version }}" \ + "${{ steps.tag.outputs.tag_name }}" diff --git a/.github/workflows/web-quality.yml b/.github/workflows/web-quality.yml new file mode 100644 index 00000000..7280b333 --- /dev/null +++ b/.github/workflows/web-quality.yml @@ -0,0 +1,59 @@ +name: Web Quality + +on: + pull_request: + branches: + - dev + - main + push: + branches: + - dev + - main + +jobs: + web-quality: + runs-on: ubuntu-latest + defaults: + run: + working-directory: apps/web + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 10 + run_install: false + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + cache-dependency-path: apps/web/pnpm-lock.yaml + + - name: Setup Firebase CLI + working-directory: . + run: npm install -g firebase-tools + + - name: Generate Data Connect SDK + working-directory: . + run: | + cp backend/dataconnect/dataconnect.dev.yaml backend/dataconnect/dataconnect.yaml + firebase dataconnect:sdk:generate --non-interactive + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Lint + run: pnpm lint + + - name: Typecheck + run: pnpm typecheck + + - name: Test + run: pnpm test + + - name: Build + run: pnpm build