Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions .github/actions/slack-notification/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}"
1 change: 1 addition & 0 deletions .github/workflows/MarketplaceRelease.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
79 changes: 78 additions & 1 deletion scripts/release/marketplaceRelease.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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. `
);
}
Loading