Create Release #100
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Create Release | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| version_type: | |
| description: 'Version type' | |
| required: true | |
| default: 'patch' | |
| type: choice | |
| options: | |
| - major | |
| - minor | |
| - patch | |
| deploy_to: | |
| description: 'Initial deployment target' | |
| required: true | |
| default: 'all' | |
| type: choice | |
| options: | |
| - all | |
| - staging | |
| - prod | |
| - none | |
| jobs: | |
| create-branch: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| permissions: | |
| contents: write | |
| outputs: | |
| new_version: ${{ steps.new-version.outputs.new_version }} | |
| branch_name: ${{ steps.new-version.outputs.branch_name }} | |
| steps: | |
| - name: Checkout main branch | |
| uses: actions/checkout@v7 | |
| with: | |
| ref: main | |
| fetch-depth: 0 | |
| # ACTIONS_TOKEN required to push to protected main branch | |
| token: ${{ secrets.ACTIONS_TOKEN || github.token }} | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v7 | |
| with: | |
| node-version: '24' | |
| cache: 'npm' | |
| - name: Get current version | |
| id: current-version | |
| run: | | |
| CURRENT_VERSION=$(node -p "require('./package.json').version") | |
| echo "current_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT | |
| - name: Calculate new version | |
| id: new-version | |
| run: | | |
| CURRENT_VERSION="${{ steps.current-version.outputs.current_version }}" | |
| VERSION_TYPE="${{ inputs.version_type }}" | |
| IFS='.' read -r major minor patch <<< "$CURRENT_VERSION" | |
| case "$VERSION_TYPE" in | |
| major) | |
| major=$((major + 1)) | |
| minor=0 | |
| patch=0 | |
| ;; | |
| minor) | |
| minor=$((minor + 1)) | |
| patch=0 | |
| ;; | |
| patch) | |
| patch=$((patch + 1)) | |
| ;; | |
| esac | |
| NEW_VERSION="${major}.${minor}.${patch}" | |
| BRANCH_NAME="release/${NEW_VERSION}" | |
| echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT | |
| echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT | |
| echo "New version: $NEW_VERSION" | |
| echo "Branch name: $BRANCH_NAME" | |
| - name: Check if branch already exists | |
| run: | | |
| BRANCH_NAME="${{ steps.new-version.outputs.branch_name }}" | |
| # Check if branch exists locally or remotely | |
| if git show-ref --verify --quiet refs/heads/$BRANCH_NAME || git show-ref --verify --quiet refs/remotes/origin/$BRANCH_NAME; then | |
| echo "❌ Branch $BRANCH_NAME already exists" | |
| exit 1 | |
| else | |
| echo "✅ Branch $BRANCH_NAME does not exist, proceeding..." | |
| fi | |
| - name: Configure Git | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| - name: Update main branch version | |
| run: | | |
| CURRENT_VERSION="${{ steps.current-version.outputs.current_version }}" | |
| NEW_VERSION="${{ steps.new-version.outputs.new_version }}" | |
| echo "Updating main branch version from $CURRENT_VERSION to $NEW_VERSION" | |
| # Update version in package.json and package-lock.json. npm version | |
| # writes both, so no install runs here and the registry is off the | |
| # release path. | |
| npm version "$NEW_VERSION" --no-git-tag-version | |
| # Commit and push to main | |
| git add package.json package-lock.json | |
| git commit -m "Release v$NEW_VERSION" | |
| git push origin main | |
| - name: Create release branch | |
| run: | | |
| BRANCH_NAME="${{ steps.new-version.outputs.branch_name }}" | |
| echo "Creating release branch: $BRANCH_NAME" | |
| # Create new branch from updated main | |
| git checkout -b "$BRANCH_NAME" | |
| # Push the release branch | |
| git push origin "$BRANCH_NAME" | |
| echo "✅ Release branch $BRANCH_NAME created and pushed" | |
| create-tag: | |
| needs: create-branch | |
| permissions: | |
| contents: write | |
| uses: RoboFinSystems/robosystems/.github/workflows/tag-release.yml@main | |
| with: | |
| branch_ref: ${{ needs.create-branch.outputs.branch_name }} | |
| product_name: 'RoboLedger App' | |
| product_description: 'Financial reporting & accounting web application' | |
| project_kind: 'Next.js frontend application' | |
| live_url: 'https://roboledger.ai' | |
| secrets: inherit | |
| # ============================================================================ | |
| # DEPLOYMENTS | |
| # Each deployment is dispatched as an independent workflow run for better | |
| # monitoring. workflow_dispatch is exempt from GitHub's "GITHUB_TOKEN events | |
| # don't trigger workflows" rule, so this works with the default token too; | |
| # ACTIONS_TOKEN is preferred when available. | |
| # ============================================================================ | |
| deploy-staging: | |
| needs: [create-tag] | |
| if: inputs.deploy_to == 'staging' || inputs.deploy_to == 'all' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| permissions: | |
| actions: write | |
| outputs: | |
| run_id: ${{ steps.dispatch.outputs.run_id }} | |
| steps: | |
| - name: Dispatch staging deployment | |
| id: dispatch | |
| env: | |
| GH_TOKEN: ${{ secrets.ACTIONS_TOKEN || github.token }} | |
| run: | | |
| echo "🚀 Dispatching staging deployment from tag ${{ needs.create-tag.outputs.tag_name }}" | |
| # Trigger the workflow | |
| gh workflow run staging.yml \ | |
| --repo ${{ github.repository }} \ | |
| --ref ${{ needs.create-tag.outputs.tag_name }} | |
| # Wait briefly for the run to be created | |
| sleep 5 | |
| # Get the run ID of the triggered workflow (dispatched at the tag, | |
| # so filtering by the tag ref avoids grabbing an unrelated run) | |
| RUN_ID=$(gh run list \ | |
| --repo ${{ github.repository }} \ | |
| --workflow=staging.yml \ | |
| --branch ${{ needs.create-tag.outputs.tag_name }} \ | |
| --limit 1 \ | |
| --json databaseId \ | |
| --jq '.[0].databaseId') | |
| echo "run_id=$RUN_ID" >> $GITHUB_OUTPUT | |
| echo "✅ Staging deployment dispatched: https://github.com/${{ github.repository }}/actions/runs/$RUN_ID" | |
| deploy-prod: | |
| needs: [create-tag, deploy-staging] | |
| if: | | |
| !cancelled() && | |
| needs.create-tag.result == 'success' && | |
| (inputs.deploy_to == 'prod' || inputs.deploy_to == 'all') && | |
| (needs.deploy-staging.result == 'success' || needs.deploy-staging.result == 'skipped') | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| permissions: | |
| actions: write | |
| outputs: | |
| run_id: ${{ steps.dispatch.outputs.run_id }} | |
| steps: | |
| - name: Wait before prod deployment | |
| if: inputs.deploy_to == 'all' | |
| run: | | |
| echo "⏳ Waiting 30 seconds before dispatching prod deployment..." | |
| echo " (prod.yml has concurrency group - will queue if staging still running)" | |
| sleep 30 | |
| - name: Dispatch prod deployment | |
| id: dispatch | |
| env: | |
| GH_TOKEN: ${{ secrets.ACTIONS_TOKEN || github.token }} | |
| run: | | |
| echo "🚀 Dispatching prod deployment from tag ${{ needs.create-tag.outputs.tag_name }}" | |
| # Trigger the workflow | |
| gh workflow run prod.yml \ | |
| --repo ${{ github.repository }} \ | |
| --ref ${{ needs.create-tag.outputs.tag_name }} | |
| # Wait briefly for the run to be created | |
| sleep 5 | |
| # Get the run ID of the triggered workflow (dispatched at the tag, | |
| # so filtering by the tag ref avoids grabbing an unrelated run) | |
| RUN_ID=$(gh run list \ | |
| --repo ${{ github.repository }} \ | |
| --workflow=prod.yml \ | |
| --branch ${{ needs.create-tag.outputs.tag_name }} \ | |
| --limit 1 \ | |
| --json databaseId \ | |
| --jq '.[0].databaseId') | |
| echo "run_id=$RUN_ID" >> $GITHUB_OUTPUT | |
| echo "✅ Prod deployment dispatched: https://github.com/${{ github.repository }}/actions/runs/$RUN_ID" | |
| create-summary: | |
| needs: [create-branch, create-tag, deploy-staging, deploy-prod] | |
| if: always() | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 2 | |
| permissions: {} | |
| steps: | |
| - name: Create summary | |
| env: | |
| STAGING_RUN_ID: ${{ needs.deploy-staging.outputs.run_id }} | |
| PROD_RUN_ID: ${{ needs.deploy-prod.outputs.run_id }} | |
| run: | | |
| echo "## RoboLedger App Release Created" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "| Property | Value |" >> $GITHUB_STEP_SUMMARY | |
| echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY | |
| echo "| Version | ${{ needs.create-tag.outputs.version }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Tag | \`${{ needs.create-tag.outputs.tag_name }}\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Branch | \`${{ needs.create-branch.outputs.branch_name }}\` |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Type | ${{ inputs.version_type }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Deploy Target | ${{ inputs.deploy_to }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Release URL:** ${{ needs.create-tag.outputs.release_url }}" >> $GITHUB_STEP_SUMMARY | |
| echo "**Live App:** [roboledger.ai](https://roboledger.ai)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| # Show deployment links | |
| if [ "${{ inputs.deploy_to }}" != "none" ]; then | |
| echo "### Deployments" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "Deployments run as **separate workflow runs** for independent monitoring." >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [ -n "$STAGING_RUN_ID" ]; then | |
| echo "- **Staging**: [View Run](https://github.com/${{ github.repository }}/actions/runs/$STAGING_RUN_ID)" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| if [ -n "$PROD_RUN_ID" ]; then | |
| echo "- **Production**: [View Run](https://github.com/${{ github.repository }}/actions/runs/$PROD_RUN_ID)" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "Monitor all deployments: \`gh run list --workflow=staging.yml\` / \`gh run list --workflow=prod.yml\`" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [ "${{ inputs.deploy_to }}" = "staging" ]; then | |
| echo "### Next Steps" >> $GITHUB_STEP_SUMMARY | |
| echo "1. Staging deployment has been triggered from tag ${{ needs.create-tag.outputs.tag_name }}" >> $GITHUB_STEP_SUMMARY | |
| echo "2. Test the release in staging environment" >> $GITHUB_STEP_SUMMARY | |
| echo "3. When ready for production: \`gh workflow run prod.yml --ref ${{ needs.create-tag.outputs.tag_name }}\`" >> $GITHUB_STEP_SUMMARY | |
| elif [ "${{ inputs.deploy_to }}" = "prod" ]; then | |
| echo "### Next Steps" >> $GITHUB_STEP_SUMMARY | |
| echo "1. Production deployment has been triggered from tag ${{ needs.create-tag.outputs.tag_name }}" >> $GITHUB_STEP_SUMMARY | |
| echo "2. The git tag is available for future deployments" >> $GITHUB_STEP_SUMMARY | |
| elif [ "${{ inputs.deploy_to }}" = "all" ]; then | |
| echo "### Next Steps" >> $GITHUB_STEP_SUMMARY | |
| echo "1. Staging deployment triggered from tag ${{ needs.create-tag.outputs.tag_name }}" >> $GITHUB_STEP_SUMMARY | |
| echo "2. Production deployment will follow (queued via concurrency group)" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "### Next Steps" >> $GITHUB_STEP_SUMMARY | |
| echo "1. Release tag ${{ needs.create-tag.outputs.tag_name }} created (no deployment)" >> $GITHUB_STEP_SUMMARY | |
| echo "2. To deploy to staging: \`gh workflow run staging.yml --ref ${{ needs.create-tag.outputs.tag_name }}\`" >> $GITHUB_STEP_SUMMARY | |
| echo "3. To deploy to production: \`gh workflow run prod.yml --ref ${{ needs.create-tag.outputs.tag_name }}\`" >> $GITHUB_STEP_SUMMARY | |
| fi |