Create Release #22
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: | |
| bump: | |
| type: choice | |
| description: 'Semver bump type' | |
| options: [patch, minor, major] | |
| default: patch | |
| custom_version: | |
| description: 'Custom version (optional, overrides bump)' | |
| required: false | |
| type: string | |
| release_type: | |
| type: choice | |
| description: 'Release type' | |
| options: [latest, prerelease] | |
| default: latest | |
| permissions: | |
| contents: write # commit version bump + tag | |
| jobs: | |
| build-package: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '18' | |
| - name: Install dependencies | |
| run: npm install | |
| - name: Install Salesforce CLI | |
| run: | | |
| npm install -g @salesforce/cli | |
| echo "$(npm config get prefix)/bin" >> $GITHUB_PATH | |
| sf --version | |
| - name: Auth via JWT (packaging org) | |
| env: | |
| SFDX_CLIENT_ID: ${{ secrets.SF_CLIENT_ID }} | |
| SFDX_JWT_KEY: ${{ secrets.SF_JWT_KEY }} | |
| SF_USERNAME: ${{ secrets.SF_PACKAGING_USERNAME }} | |
| run: | | |
| echo "$SFDX_JWT_KEY" > server.key | |
| sf org login jwt \ | |
| --client-id "$SFDX_CLIENT_ID" \ | |
| --jwt-key-file server.key \ | |
| --username "$SF_USERNAME" \ | |
| --alias pkgorg --set-default | |
| rm -f server.key | |
| - name: Read and calculate version | |
| id: version | |
| run: | | |
| # Read current version from package.json | |
| CUR=$(node -p "require('./package.json').version") | |
| MAJOR=$(echo "$CUR" | cut -d. -f1) | |
| MINOR=$(echo "$CUR" | cut -d. -f2) | |
| PATCH=$(echo "$CUR" | cut -d. -f3) | |
| if [ -n "${{ github.event.inputs.custom_version }}" ]; then | |
| NEW="${{ github.event.inputs.custom_version }}" | |
| echo "Using custom version: $NEW" | |
| else | |
| case "${{ github.event.inputs.bump }}" in | |
| major) MAJOR=$((MAJOR+1)); MINOR=0; PATCH=0 ;; | |
| minor) MINOR=$((MINOR+1)); PATCH=0 ;; | |
| patch) PATCH=$((PATCH+1)) ;; | |
| esac | |
| NEW="$MAJOR.$MINOR.$PATCH" | |
| fi | |
| # Debug: Show all input values | |
| echo "🔍 DEBUG: Input values:" | |
| echo " bump: '${{ github.event.inputs.bump }}'" | |
| echo " custom_version: '${{ github.event.inputs.custom_version }}'" | |
| echo " release_type: '${{ github.event.inputs.release_type }}'" | |
| echo " Calculated NEW before beta check: '$NEW'" | |
| # Add prerelease suffix if release type is prerelease | |
| if [ "${{ github.event.inputs.release_type }}" == "prerelease" ]; then | |
| NEW="${NEW}-beta" | |
| echo " Added prerelease suffix: '$NEW'" | |
| else | |
| echo " No prerelease suffix added (release_type is not 'prerelease')" | |
| fi | |
| echo "current=$CUR" >> $GITHUB_OUTPUT | |
| echo "new=$NEW" >> $GITHUB_OUTPUT | |
| echo "tag=v$NEW" >> $GITHUB_OUTPUT | |
| echo "release_type=${{ github.event.inputs.release_type }}" >> $GITHUB_OUTPUT | |
| echo "Current version: $CUR" | |
| echo "New version: $NEW" | |
| echo "Release type: ${{ github.event.inputs.release_type }}" | |
| - name: Update package.json version | |
| run: | | |
| NEW_VERSION="${{ steps.version.outputs.new }}" | |
| npm version $NEW_VERSION --no-git-tag-version | |
| echo "Updated package.json to version $NEW_VERSION" | |
| - name: Update package.xml version | |
| run: | | |
| NEW_VERSION="${{ steps.version.outputs.new }}" | |
| API_VERSION=$(echo $NEW_VERSION | cut -d. -f1) | |
| sed -i "s/<version>.*<\/version>/<version>$API_VERSION.0<\/version>/" manifest/package.xml | |
| echo "Updated package.xml API version to $API_VERSION.0" | |
| - name: Update sfdx-project.json version | |
| run: | | |
| NEW_VERSION="${{ steps.version.outputs.new }}" | |
| # Extract numeric version (remove prerelease suffix if present) for versionNumber | |
| NUMERIC_VERSION=$(echo "$NEW_VERSION" | sed 's/-.*$//') | |
| # Update versionName in sfdx-project.json | |
| sed -i "s/\"versionName\": \"ver [^\"]*\"/\"versionName\": \"ver $NEW_VERSION\"/" sfdx-project.json | |
| # Update versionNumber in sfdx-project.json (format: MAJOR.MINOR.PATCH.NEXT) | |
| sed -i "s/\"versionNumber\": \"[^\"]*\"/\"versionNumber\": \"$NUMERIC_VERSION.NEXT\"/" sfdx-project.json | |
| echo "Updated sfdx-project.json version to $NEW_VERSION (numeric: $NUMERIC_VERSION)" | |
| - name: Create package | |
| run: | | |
| # Create package directory | |
| mkdir -p package | |
| # Copy source files | |
| cp -r force-app package/ | |
| cp manifest/package.xml package/ | |
| cp README.md package/ | |
| cp docs/PACKAGE-README.md package/ 2>/dev/null || true | |
| # Create package zip | |
| cd package | |
| zip -r ../REST-API-Library-v${{ steps.version.outputs.new }}.zip . | |
| cd .. | |
| echo "Created REST-API-Library-v${{ steps.version.outputs.new }}.zip" | |
| # Clean up package directory | |
| rm -rf package | |
| echo "Cleaned up temporary package directory" | |
| - name: Convert to MDAPI | |
| run: | | |
| sf project convert source --root-dir force-app --output-dir mdapi_out | |
| - name: Deploy to packaging org | |
| run: | | |
| echo "Deploying converted metadata to packaging org..." | |
| echo "Source directory: mdapi_out" | |
| echo "Target org: pkgorg" | |
| set +e # Don't exit on error, we'll handle it manually | |
| DEPLOY_OUTPUT=$(sf project deploy start --metadata-dir mdapi_out --target-org pkgorg --wait 60 --ignore-conflicts 2>&1) | |
| DEPLOY_EXIT_CODE=$? | |
| set -e # Re-enable exit on error | |
| echo "Deploy exit code: $DEPLOY_EXIT_CODE" | |
| echo "Deploy output:" | |
| echo "$DEPLOY_OUTPUT" | |
| if [ $DEPLOY_EXIT_CODE -eq 0 ]; then | |
| echo "✅ Successfully deployed to packaging org" | |
| else | |
| echo "❌ ERROR: Deployment failed with exit code $DEPLOY_EXIT_CODE" | |
| echo "💡 Check the output above for specific error details" | |
| exit 1 | |
| fi | |
| - name: Run tests and check code coverage | |
| run: | | |
| echo "Running tests for package-specific test classes: NebulaAdapter_Test, RestLibTests" | |
| # Run only the test classes that are part of this package | |
| set +e # Don't exit on error, we'll handle it manually | |
| # Capture both stdout and stderr, but ignore warnings in stderr | |
| sf apex test run --class-names NebulaAdapter_Test --class-names RestLibTests --target-org pkgorg --result-format json --code-coverage --wait 10 > test_result.json 2>test_errors.log | |
| TEST_EXIT_CODE=$? | |
| set -e # Re-enable exit on error | |
| # Check if there were any real errors (not just warnings) | |
| if [ -s test_errors.log ]; then | |
| echo "🔍 Debug: Checking for errors vs warnings..." | |
| cat test_errors.log | |
| # If the only content is warnings about array format, we can ignore it | |
| if grep -q "array arguments has changed" test_errors.log && ! grep -v "array arguments has changed\|Warning:" test_errors.log; then | |
| echo "⚠️ WARNING: Only array format warnings detected, ignoring..." | |
| TEST_EXIT_CODE=0 # Treat as success if only warnings | |
| fi | |
| fi | |
| echo "Test execution exit code: $TEST_EXIT_CODE" | |
| echo "Test execution output:" | |
| # Read from file to avoid broken pipe issues | |
| cat test_result.json | |
| # Check if test execution was successful | |
| # Note: sf apex test run may return non-zero exit code for warnings, but tests might still pass | |
| # We'll check the actual test results in the JSON output instead of relying solely on exit code | |
| if [ $TEST_EXIT_CODE -ne 0 ]; then | |
| echo "⚠️ WARNING: Test execution returned exit code $TEST_EXIT_CODE" | |
| echo "This might be due to CLI warnings. Checking actual test results..." | |
| fi | |
| # Check if we have valid JSON output | |
| echo "🔍 Debug: Checking JSON validity..." | |
| if ! jq -e '.result' test_result.json > /dev/null 2>&1; then | |
| echo "❌ ERROR: Invalid test result format" | |
| echo "Raw output:" | |
| cat test_result.json | |
| echo "🔍 Debug: jq command failed. Checking if jq is available..." | |
| which jq || echo "jq not found in PATH" | |
| jq --version || echo "jq version check failed" | |
| exit 1 | |
| else | |
| echo "✅ JSON format is valid" | |
| fi | |
| # Extract test summary | |
| if jq -e '.result.summary' test_result.json > /dev/null 2>&1; then | |
| SUMMARY=$(jq -r '.result.summary' test_result.json) | |
| echo "Test Summary: $SUMMARY" | |
| # Show detailed test counts | |
| PASSING=$(jq -r '.result.summary.passing // 0' test_result.json) | |
| FAILING=$(jq -r '.result.summary.failing // 0' test_result.json) | |
| SKIPPED=$(jq -r '.result.summary.skipped // 0' test_result.json) | |
| TESTS_RAN=$(jq -r '.result.summary.testsRan // 0' test_result.json) | |
| echo "📈 Test Results: $PASSING passed, $FAILING failed, $SKIPPED skipped (Total: $TESTS_RAN)" | |
| fi | |
| # Extract coverage percentage - try multiple possible locations in the JSON | |
| COVERAGE="" | |
| if jq -e '.result.summary.testRunCoverage' test_result.json > /dev/null 2>&1; then | |
| COVERAGE=$(jq -r '.result.summary.testRunCoverage' test_result.json | sed 's/%//') | |
| echo "Code coverage (test run): $COVERAGE%" | |
| elif jq -e '.result.coverage.coverage' test_result.json > /dev/null 2>&1; then | |
| COVERAGE=$(jq -r '.result.coverage.coverage[].percent' test_result.json) | |
| echo "Code coverage (per class): $COVERAGE%" | |
| else | |
| echo "⚠️ WARNING: Could not extract coverage information from test results" | |
| fi | |
| # Check if coverage meets minimum requirement (75%) | |
| if [ -n "$COVERAGE" ]; then | |
| MIN_COVERAGE=75 | |
| if (( $(echo "$COVERAGE < $MIN_COVERAGE" | bc -l) )); then | |
| echo "" | |
| echo "❌ COVERAGE FAILURE: Code coverage $COVERAGE% is below minimum requirement of $MIN_COVERAGE%" | |
| echo "" | |
| echo "📊 COVERAGE ANALYSIS:" | |
| echo " Current Coverage: $COVERAGE%" | |
| echo " Required Coverage: $MIN_COVERAGE%" | |
| echo " Coverage Gap: $((MIN_COVERAGE - COVERAGE))%" | |
| echo "" | |
| echo "🔍 TEST CLASSES BEING VALIDATED:" | |
| echo " - NebulaAdapter_Test" | |
| echo " - RestLibTests" | |
| echo "" | |
| echo "💡 TO FIX THIS ISSUE:" | |
| echo " 1. Add more test methods to cover uncovered code paths" | |
| echo " 2. Improve existing test methods to cover more scenarios" | |
| echo " 3. Review uncovered lines in the test results above" | |
| echo " 4. Ensure all public methods and critical code paths are tested" | |
| echo "" | |
| echo "📋 NEXT STEPS:" | |
| echo " - Check the detailed coverage information in the test output above" | |
| echo " - Look for 'uncoveredLines' in the JSON output to see which lines need testing" | |
| echo " - Add test methods for any missing scenarios" | |
| echo " - Re-run the workflow after improving test coverage" | |
| echo "" | |
| exit 1 | |
| else | |
| echo "✅ SUCCESS: Code coverage $COVERAGE% meets minimum requirement of $MIN_COVERAGE%" | |
| fi | |
| fi | |
| # Check for test failures - this is the real test validation | |
| if jq -e '.result.failures' test_result.json > /dev/null 2>&1; then | |
| FAILURES=$(jq -r '.result.failures' test_result.json) | |
| if [ "$FAILURES" != "0" ] && [ "$FAILURES" != "null" ]; then | |
| echo "❌ ERROR: $FAILURES test(s) failed" | |
| echo "Test failures details:" | |
| jq -r '.result.tests[] | select(.Outcome == "Fail") | " - \(.MethodName): \(.Message)"' test_result.json 2>/dev/null || echo "Could not extract failure details" | |
| exit 1 | |
| fi | |
| fi | |
| # Additional validation: Check if any tests actually ran and passed | |
| if jq -e '.result.summary' test_result.json > /dev/null 2>&1; then | |
| TESTS_RAN=$(jq -r '.result.summary.testsRan // 0' test_result.json) | |
| PASSING=$(jq -r '.result.summary.passing // 0' test_result.json) | |
| FAILING=$(jq -r '.result.summary.failing // 0' test_result.json) | |
| if [ "$TESTS_RAN" = "0" ]; then | |
| echo "❌ ERROR: No tests were executed" | |
| exit 1 | |
| fi | |
| if [ "$FAILING" != "0" ] && [ "$FAILING" != "null" ]; then | |
| echo "❌ ERROR: $FAILING test(s) failed out of $TESTS_RAN total tests" | |
| exit 1 | |
| fi | |
| echo "✅ SUCCESS: All $PASSING tests passed (0 failures)" | |
| else | |
| echo "❌ ERROR: Could not extract test summary from results" | |
| exit 1 | |
| fi | |
| # Clean up temporary files | |
| rm -f test_result.json test_errors.log | |
| - name: Create package and version | |
| id: package_version | |
| env: | |
| SF_PACKAGE1_ID: ${{ vars.SF_PACKAGE1_ID }} | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| # Check if package ID exists, if not create a new package | |
| if [ -z "$SF_PACKAGE1_ID" ] || [ "$SF_PACKAGE1_ID" = "NULL" ] || [ "$SF_PACKAGE1_ID" = "null" ] || [ "$SF_PACKAGE1_ID" = "" ]; then | |
| echo "No package ID found, creating new package..." | |
| # Debug: Check authentication and org status | |
| echo "Checking authentication..." | |
| sf org list || echo "No orgs found" | |
| # Debug: Check if we're in the right org | |
| echo "Current org info:" | |
| sf org display || echo "No org display available" | |
| # Debug: Try a simple command first | |
| echo "Testing basic sf command..." | |
| sf --version || echo "SF CLI not working" | |
| # Create a new package with more debugging | |
| echo "Creating package with command:" | |
| echo "sf package create --name 'REST API Library' --description 'A comprehensive Salesforce Apex library for making REST API callouts' --package-type Unlocked --path force-app --target-dev-hub pkgorg --json" | |
| PACKAGE_OUTPUT=$(sf package create \ | |
| --name "REST API Library" \ | |
| --description "A comprehensive Salesforce Apex library for making REST API callouts" \ | |
| --package-type Unlocked \ | |
| --path force-app \ | |
| --target-dev-hub pkgorg \ | |
| --json 2>&1) | |
| echo "Package create exit code: $?" | |
| echo "Package create output:" | |
| echo "$PACKAGE_OUTPUT" | |
| # Extract package ID | |
| PACKAGE_ID=$(echo "$PACKAGE_OUTPUT" | jq -r '.result.Id') | |
| if [ "$PACKAGE_ID" != "null" ] && [ -n "$PACKAGE_ID" ]; then | |
| echo "✅ Created new package with ID: $PACKAGE_ID" | |
| # Update GitHub repository variable automatically | |
| echo "Updating GitHub repository variable..." | |
| curl -X PATCH \ | |
| -H "Accept: application/vnd.github+json" \ | |
| -H "Authorization: Bearer $GITHUB_TOKEN" \ | |
| -H "X-GitHub-Api-Version: 2022-11-28" \ | |
| "https://api.github.com/repos/${{ github.repository }}/actions/variables/SF_PACKAGE1_ID" \ | |
| -d "{\"name\":\"SF_PACKAGE1_ID\",\"value\":\"$PACKAGE_ID\"}" && echo "✅ GitHub variable updated successfully" || echo "⚠️ Failed to update GitHub variable (you can update manually)" | |
| # Set the package ID for this run | |
| SF_PACKAGE1_ID="$PACKAGE_ID" | |
| else | |
| echo "ERROR: Failed to create package" | |
| echo "Full output: $PACKAGE_OUTPUT" | |
| exit 1 | |
| fi | |
| else | |
| echo "Using existing package ID: $SF_PACKAGE1_ID" | |
| fi | |
| # Create a new package version | |
| echo "Creating package version for package: $SF_PACKAGE1_ID" | |
| echo "Command: sf package version create --package '$SF_PACKAGE1_ID' --installation-key-bypass --wait 10 --target-dev-hub pkgorg --json" | |
| # Run the command and capture both output and exit code | |
| set +e # Don't exit on error, we'll handle it manually | |
| PACKAGE_VERSION_OUTPUT=$(sf package version create \ | |
| --package "$SF_PACKAGE1_ID" \ | |
| --installation-key-bypass \ | |
| --wait 10 \ | |
| --target-dev-hub pkgorg \ | |
| --json 2>&1) | |
| PACKAGE_VERSION_EXIT_CODE=$? | |
| set -e # Re-enable exit on error | |
| echo "Package version create exit code: $PACKAGE_VERSION_EXIT_CODE" | |
| echo "Package version create output:" | |
| echo "$PACKAGE_VERSION_OUTPUT" | |
| # Check if the command was successful | |
| if [ $PACKAGE_VERSION_EXIT_CODE -eq 0 ] && echo "$PACKAGE_VERSION_OUTPUT" | jq -e '.result.SubscriberPackageVersionId' > /dev/null 2>&1; then | |
| # Extract the package version ID and create installation URLs | |
| PACKAGE_VERSION_ID=$(echo "$PACKAGE_VERSION_OUTPUT" | jq -r '.result.SubscriberPackageVersionId') | |
| PRODUCTION_URL="https://login.salesforce.com/packaging/installPackage.apexp?p0=$PACKAGE_VERSION_ID" | |
| SANDBOX_URL="https://test.salesforce.com/packaging/installPackage.apexp?p0=$PACKAGE_VERSION_ID" | |
| echo "package_version_id=$PACKAGE_VERSION_ID" >> $GITHUB_OUTPUT | |
| echo "production_url=$PRODUCTION_URL" >> $GITHUB_OUTPUT | |
| echo "sandbox_url=$SANDBOX_URL" >> $GITHUB_OUTPUT | |
| echo "package_id=$SF_PACKAGE1_ID" >> $GITHUB_OUTPUT | |
| echo "✅ Created package version: $PACKAGE_VERSION_ID" | |
| echo "Production URL: $PRODUCTION_URL" | |
| echo "Sandbox URL: $SANDBOX_URL" | |
| # Promote package version to released status if it's a latest release | |
| if [ "${{ steps.version.outputs.release_type }}" == "latest" ]; then | |
| echo "🚀 Promoting package version to released status..." | |
| PROMOTE_OUTPUT=$(sf package version promote \ | |
| --package "$SF_PACKAGE1_ID@$PACKAGE_VERSION_ID" \ | |
| --target-dev-hub pkgorg \ | |
| --json 2>&1) | |
| PROMOTE_EXIT_CODE=$? | |
| echo "Package version promote exit code: $PROMOTE_EXIT_CODE" | |
| echo "Package version promote output:" | |
| echo "$PROMOTE_OUTPUT" | |
| if [ $PROMOTE_EXIT_CODE -eq 0 ]; then | |
| echo "✅ Successfully promoted package version to released status" | |
| else | |
| echo "⚠️ WARNING: Failed to promote package version to released status" | |
| echo "Package will remain as prerelease version" | |
| fi | |
| else | |
| echo "ℹ️ Package version remains as prerelease (release_type is prerelease)" | |
| fi | |
| else | |
| echo "❌ ERROR: Failed to create package version" | |
| echo "Exit code: $PACKAGE_VERSION_EXIT_CODE" | |
| echo "Full output: $PACKAGE_VERSION_OUTPUT" | |
| # Try to extract error details from JSON output | |
| if echo "$PACKAGE_VERSION_OUTPUT" | jq -e '.message' > /dev/null 2>&1; then | |
| ERROR_MESSAGE=$(echo "$PACKAGE_VERSION_OUTPUT" | jq -r '.message') | |
| echo "Error message: $ERROR_MESSAGE" | |
| fi | |
| if echo "$PACKAGE_VERSION_OUTPUT" | jq -e '.result[0].error' > /dev/null 2>&1; then | |
| ERROR_DETAILS=$(echo "$PACKAGE_VERSION_OUTPUT" | jq -r '.result[0].error') | |
| echo "Error details: $ERROR_DETAILS" | |
| fi | |
| # Check for common issues | |
| if echo "$PACKAGE_VERSION_OUTPUT" | grep -i "permission" > /dev/null; then | |
| echo "💡 Possible permission issue - check if the user has Package Creation permissions" | |
| fi | |
| if echo "$PACKAGE_VERSION_OUTPUT" | grep -i "validation" > /dev/null; then | |
| echo "💡 Possible validation error - check if all metadata is valid" | |
| fi | |
| if echo "$PACKAGE_VERSION_OUTPUT" | grep -i "coverage" > /dev/null; then | |
| echo "💡 Possible code coverage issue - ensure all code has adequate test coverage" | |
| fi | |
| exit 1 | |
| fi | |
| - name: Build changelog | |
| run: | | |
| # Get previous tag | |
| git fetch --tags --quiet || true | |
| PREV=$(git tag --sort=-creatordate | head -1) | |
| if [ -n "$PREV" ]; then | |
| RANGE="$PREV..HEAD" | |
| COMPARE_LINK="https://github.com/${{ github.repository }}/compare/$PREV...${{ steps.version.outputs.tag }}" | |
| else | |
| FIRST=$(git rev-list --max-parents=0 HEAD) | |
| RANGE="$FIRST..HEAD" | |
| COMPARE_LINK="https://github.com/${{ github.repository }}/compare/$FIRST...${{ steps.version.outputs.tag }}" | |
| fi | |
| { | |
| echo "## Release ${{ steps.version.outputs.tag }}" | |
| echo | |
| echo "**Package:** REST-API-Library-v${{ steps.version.outputs.new }}.zip" | |
| echo "**Previous Version:** ${{ steps.version.outputs.current }}" | |
| echo "**Release Type:** ${{ steps.version.outputs.release_type }}" | |
| echo "**Package Version ID:** ${{ steps.package_version.outputs.package_version_id }}" | |
| echo | |
| echo "### Installation" | |
| echo "- **Production:** [${{ steps.package_version.outputs.production_url }}](${{ steps.package_version.outputs.production_url }})" | |
| echo "- **Sandbox:** [${{ steps.package_version.outputs.sandbox_url }}](${{ steps.package_version.outputs.sandbox_url }})" | |
| echo "- **CLI:** \`sf package install --package ${{ steps.package_version.outputs.package_version_id }} --wait 10 --installation-key-bypass\`" | |
| echo | |
| echo "### Changes" | |
| if [ -n "$PREV" ]; then | |
| git log --pretty=format:'- %s (%h) — %an' $RANGE | |
| else | |
| echo "- Initial release" | |
| fi | |
| echo | |
| echo "[Compare changes](${COMPARE_LINK})" | |
| } > release_notes.md | |
| echo "Generated changelog from $RANGE" | |
| - name: Commit version changes | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GIT_AUTHOR_NAME: github-actions | |
| GIT_AUTHOR_EMAIL: github-actions@users.noreply.github.com | |
| GIT_COMMITTER_NAME: github-actions | |
| GIT_COMMITTER_EMAIL: github-actions@users.noreply.github.com | |
| run: | | |
| set -e | |
| git add package.json manifest/package.xml sfdx-project.json | |
| if ! git diff --quiet --staged; then | |
| git commit -m "chore(release): bump version to ${{ steps.version.outputs.new }}" | |
| fi | |
| git tag -a "${{ steps.version.outputs.tag }}" -m "Release ${{ steps.version.outputs.new }}" | |
| git push https://x-access-token:${GITHUB_TOKEN}@github.com/${{ github.repository }} HEAD:${{ github.ref_name }} | |
| git push https://x-access-token:${GITHUB_TOKEN}@github.com/${{ github.repository }} "${{ steps.version.outputs.tag }}" | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: release-${{ steps.version.outputs.tag }} | |
| path: | | |
| REST-API-Library-v${{ steps.version.outputs.new }}.zip | |
| release_notes.md | |
| retention-days: 30 | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ steps.version.outputs.tag }} | |
| name: REST API Library ${{ steps.version.outputs.tag }} | |
| body_path: release_notes.md | |
| files: | | |
| REST-API-Library-v${{ steps.version.outputs.new }}.zip | |
| prerelease: ${{ steps.version.outputs.release_type == 'prerelease' }} | |
| latest: ${{ steps.version.outputs.release_type == 'latest' }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Update README with release info | |
| run: | | |
| PACKAGE_FILE="REST-API-Library-v${{ steps.version.outputs.new }}.zip" | |
| { | |
| echo "<!--LATEST-RELEASE-START-->" | |
| echo "**Version:** ${{ steps.version.outputs.new }} " | |
| echo "**Tag:** ${{ steps.version.outputs.tag }} " | |
| echo "**Release Type:** ${{ steps.version.outputs.release_type }} " | |
| echo "**Package Version ID:** ${{ steps.package_version.outputs.package_version_id }} " | |
| echo "**Package:** [$PACKAGE_FILE](https://github.com/${{ github.repository }}/releases/download/${{ steps.version.outputs.tag }}/$PACKAGE_FILE) " | |
| echo "**Release Date:** $(date -u +"%Y-%m-%d") " | |
| echo "" | |
| echo "### Quick Install" | |
| echo "- **Production:** [${{ steps.package_version.outputs.production_url }}](${{ steps.package_version.outputs.production_url }})" | |
| echo "- **Sandbox:** [${{ steps.package_version.outputs.sandbox_url }}](${{ steps.package_version.outputs.sandbox_url }})" | |
| echo "- **CLI:** \`sf package install --package ${{ steps.package_version.outputs.package_version_id }} --wait 10 --installation-key-bypass\`" | |
| echo "" | |
| echo "<details><summary>Change summary</summary>" | |
| echo "" | |
| # Skip first 5 lines (header) from notes if desired: | |
| awk 'NR>5 {print}' release_notes.md | |
| echo "</details>" | |
| echo "<!--LATEST-RELEASE-END-->" | |
| } > latest_block.md | |
| # Update README with the new block (replace entire section) | |
| awk ' | |
| BEGIN{printing=1; in_section=0} | |
| /<!--LATEST-RELEASE-START-->/ {print; system("cat latest_block.md"); printing=0; in_section=1; next} | |
| /<!--LATEST-RELEASE-END-->/ {print; printing=1; in_section=0; next} | |
| in_section==0 && printing==1 {print} | |
| ' README.md > README.md.new | |
| mv README.md.new README.md | |
| rm latest_block.md | |
| echo "Updated README.md with latest release information" | |
| - name: Commit README update | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GIT_AUTHOR_NAME: github-actions | |
| GIT_AUTHOR_EMAIL: github-actions@users.noreply.github.com | |
| GIT_COMMITTER_NAME: github-actions | |
| GIT_COMMITTER_EMAIL: github-actions@users.noreply.github.com | |
| run: | | |
| set -e | |
| if ! git diff --quiet; then | |
| git add README.md | |
| git commit -m "docs: update README for ${{ steps.version.outputs.tag }}" | |
| git push https://x-access-token:${GITHUB_TOKEN}@github.com/${{ github.repository }} HEAD:${{ github.ref_name }} | |
| echo "Committed README update" | |
| else | |
| echo "No README changes to commit" | |
| fi | |
| - name: Summary | |
| run: | | |
| echo "## Release Created Successfully!" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Version:** ${{ steps.version.outputs.new }}" >> $GITHUB_STEP_SUMMARY | |
| echo "**Previous Version:** ${{ steps.version.outputs.current }}" >> $GITHUB_STEP_SUMMARY | |
| echo "**Release Type:** ${{ steps.version.outputs.release_type }}" >> $GITHUB_STEP_SUMMARY | |
| echo "**Bump Type:** ${{ github.event.inputs.bump }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Artifacts Created" >> $GITHUB_STEP_SUMMARY | |
| echo "- Package zip file" >> $GITHUB_STEP_SUMMARY | |
| echo "- Salesforce package version: ${{ steps.package_version.outputs.package_version_id }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- Production URL: ${{ steps.package_version.outputs.production_url }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- Sandbox URL: ${{ steps.package_version.outputs.sandbox_url }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- Release notes" >> $GITHUB_STEP_SUMMARY | |
| echo "- Version file" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Next Steps" >> $GITHUB_STEP_SUMMARY | |
| echo "- GitHub release created and README updated automatically!" >> $GITHUB_STEP_SUMMARY | |
| echo "- Package version created and ready for installation!" >> $GITHUB_STEP_SUMMARY |