diff --git a/.github/actions/slack-notification/action.yml b/.github/actions/slack-notification/action.yml index dc7d4af7b..8fe4fdd5b 100644 --- a/.github/actions/slack-notification/action.yml +++ b/.github/actions/slack-notification/action.yml @@ -18,7 +18,8 @@ runs: - name: Send Slack notification uses: slackapi/slack-github-action@485a9d42d3a73031f12ec201c457e2162c45d02d # v2 with: - channel-id: ${{ inputs.channel-id }} - slack-message: ${{ inputs.message }} - env: - SLACK_BOT_TOKEN: ${{ inputs.bot-token }} + method: chat.postMessage + token: ${{ inputs.bot-token }} + payload: | + channel: "${{ inputs.channel-id }}" + text: "${{ inputs.message }}" diff --git a/.github/workflows/MarketplaceRelease.yml b/.github/workflows/MarketplaceRelease.yml index d65ceee98..3b33cbbe2 100644 --- a/.github/workflows/MarketplaceRelease.yml +++ b/.github/workflows/MarketplaceRelease.yml @@ -49,6 +49,7 @@ jobs: CPAPI_PASS_PROD: ${{ secrets.CPAPI_PASS_PROD }} TAG: ${{ steps.variables.outputs.tag }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + MENDIX_PAT_TOKEN: ${{ secrets.MENDIX_PAT_TOKEN }} - name: "Send slack msg on failure" if: ${{ failure() }} uses: ./.github/actions/slack-notification diff --git a/scripts/release/marketplaceRelease.js b/scripts/release/marketplaceRelease.js index d902b424b..4e1e03f65 100644 --- a/scripts/release/marketplaceRelease.js +++ b/scripts/release/marketplaceRelease.js @@ -3,7 +3,7 @@ const { join } = require("path"); const config = { appStoreUrl: "https://appstore.home.mendix.com/rest/packagesapi/v2", - contributorUrl: "https://contributor.mendixcloud.com/apis/v1", + contributorUrl: "https://contributor.mendix.com/apis/v1", // This one, for some reasons, needs to be added as OpenID header to contributor request. // The open id value (a39025a8-55b8-4532-bc5d-4e74901d11f9) is taken from widgets@mendix.com // account and could be found at Profile -> Advanced -> Personal Info -> View My Data -> Open id @@ -44,6 +44,8 @@ async function uploadModuleToAppStore(pkgName, marketplaceId, version, minimumMX const postResponse = await createDraft(marketplaceId, version, minimumMXVersion); await publishDraft(postResponse.UUID); console.log(`Successfully uploaded ${pkgName} to the Mendix Marketplace.`); + + await verifyReleasePublished(marketplaceId, version, pkgName); } catch (error) { error.message = `Failed uploading ${pkgName} to appstore with error: ${error.message}`; throw error; @@ -176,3 +178,78 @@ function packageMetadata() { const { name, widgetName, version, marketplace } = require(pkgPath); return { name, widgetName, version, marketplace }; } + +async function verifyReleasePublished(contentId, expectedVersion, pkgName) { + const normalizedExpectedVersion = expectedVersion.startsWith("v") ? expectedVersion.substring(1) : expectedVersion; + + console.log(`Verifying release ${normalizedExpectedVersion} is published for content ID ${contentId}...`); + + const patToken = process.env.MENDIX_PAT_TOKEN; + if (!patToken) { + console.warn("WARNING: MENDIX_PAT_TOKEN environment variable is not set. Skipping release verification."); + return; + } + + const maxRetries = 10; + const retryDelayMs = 10000; + + for (let attempt = 1; attempt <= maxRetries; attempt++) { + console.log(`Verification attempt ${attempt}/${maxRetries}: Checking for version ${expectedVersion}`); + + try { + // Call the Mendix Content API to get all versions + const versionsResponse = await nodefetch( + `https://marketplace-api.mendix.com/v1/content/${contentId}/versions`, + { + method: "GET", + headers: { + Accept: "application/json", + Authorization: `MxToken ${patToken}` + } + } + ); + + if (!versionsResponse.ok) { + const errorText = await versionsResponse.text(); + throw new Error( + `Content API returned status ${versionsResponse.status}: ${versionsResponse.statusText}. Response: ${errorText}` + ); + } + + const responseData = await versionsResponse.json(); + + if (!responseData.items || !Array.isArray(responseData.items)) { + throw new Error(`Unexpected API response structure: ${JSON.stringify(responseData)}`); + } + + const versions = responseData.items; + + const versionFound = versions.some(v => v.versionNumber === normalizedExpectedVersion); + + if (versionFound) { + console.log( + `✓ Successfully verified: Version ${normalizedExpectedVersion} is published on Mendix Marketplace!` + ); + const matchedVersion = versions.find(v => v.versionNumber === normalizedExpectedVersion); + console.log(`Version ID: ${matchedVersion.versionId}`); + console.log(`Publication Date: ${matchedVersion.publicationDate}`); + return; + } + + if (attempt < maxRetries) { + await new Promise(resolve => setTimeout(resolve, retryDelayMs)); + } + } catch (error) { + console.error(`Error during verification attempt ${attempt}: ${error.message}`); + if (attempt < maxRetries) { + await new Promise(resolve => setTimeout(resolve, retryDelayMs)); + } + } + } + + throw new Error( + `Release verification FAILED: Version ${normalizedExpectedVersion} for ${pkgName} (content ID: ${contentId}) ` + + `was not found on Mendix Marketplace after ${maxRetries} attempts. ` + + `The publish step reported success, but the version is not publicly available. ` + ); +}