nx/packages/gradle/project-graph/publish-maven.ts
Emily Xiong b377c96d99
feat(java): add gradle kotlin plugin (#29464)
- [x] change init to create `createNodes` instead
- [x] unit tests
- [x] test-ci
- [x] test on windows
- [x] help metadata
- [x] external nodes

TODO:
- add publish executor?
- publish to maven central?

<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->
currently, it uses [project report
plugin](https://docs.gradle.org/current/userguide/project_report_plugin.html).
- pro: no need to maintain this plugin
- con: this plugin gives limited information

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->
change the project report plugin to @nxn/gradle/plugin-v1
now the @nx/gradle plugin will use project graph plugin
(dev.nx.gradle.project-graph) created in this pr.
this plugin will create json file that is exactly what nx project grpah
expected.

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
2025-04-23 13:13:25 -04:00

134 lines
3.8 KiB
TypeScript

import axios from 'axios';
import * as fs from 'fs';
import * as FormData from 'form-data';
function parseArgs() {
const args = process.argv.slice(2);
const result: Record<string, string> = {};
args.forEach((arg) => {
const [key, value] = arg.replace(/^--/, '').split('=');
result[key] = value;
});
return result;
}
async function publishToMavenApi(
username: string,
password: string,
deploymentZipPath = 'deployment.zip'
) {
const token = Buffer.from(`${username}:${password}`).toString('base64');
console.log(`📦 Publishing to Maven Central...`);
const url = 'https://central.sonatype.com/api/v1/publisher/upload';
const form = new FormData();
form.append('bundle', fs.createReadStream(deploymentZipPath));
let uploadId: string;
try {
const response = await axios.post(url, form, {
headers: {
Authorization: `Basic ${token}`,
...form.getHeaders(),
},
});
uploadId = response.data.toString().trim();
console.log(`✅ Upload ID: ${uploadId}`);
} catch (err: any) {
console.error('🚫 Upload failed:', err.response?.data || err.message);
process.exit(1);
}
let currentStatus = await getUploadStatus(uploadId, token);
if (['PENDING', 'VALIDATING', 'PUBLISHING'].includes(currentStatus)) {
currentStatus = await retryUntilValidatedOrPublished(
currentStatus,
uploadId,
token
);
}
if (!['VALIDATED', 'PUBLISHED'].includes(currentStatus)) {
console.error(`🚫 Upload failed with final status: ${currentStatus}`);
process.exit(1);
}
console.log(`📦 Upload is ${currentStatus}, proceeding to deploy...`);
if (currentStatus === 'PUBLISHED') {
console.log('✅ Already published, skipping deployment.');
return;
}
const deployUrl = `https://central.sonatype.com/api/v1/publisher/deployment/${uploadId}`;
try {
const deployRes = await axios.post(deployUrl, null, {
headers: { Authorization: `Basic ${token}` },
});
console.log(`🚀 Deployment response: ${deployRes.data}`);
} catch (err: any) {
console.error('🚫 Deployment failed:', err.response?.data || err.message);
process.exit(1);
}
}
async function getUploadStatus(
uploadId: string,
token: string
): Promise<string> {
const url = `https://central.sonatype.com/api/v1/publisher/status?id=${uploadId}`;
try {
const response = await axios.post(url, null, {
headers: { Authorization: `Basic ${token}` },
});
const state = response.data.deploymentState;
console.log(`📡 Current deployment state: ${state}`);
return state;
} catch (err: any) {
console.error(
'🚫 Failed to get status:',
err.response?.data || err.message
);
return 'FAILED';
}
}
async function retryUntilValidatedOrPublished(
currentStatus: string,
uploadId: string,
token: string,
retries = 10,
delay = 10_000
): Promise<string> {
for (let i = 0; i < retries; i++) {
console.log(`🔁 Checking status (attempt ${i + 1}/${retries})...`);
await sleep(delay);
currentStatus = await getUploadStatus(uploadId, token);
if (['VALIDATED', 'PUBLISHED', 'FAILED'].includes(currentStatus)) break;
}
return currentStatus;
}
function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
// Entry
(async function main() {
let { username, password, deploymentZipPath } = parseArgs();
username = username || process.env.MAVEN_USERNAME;
password = password || process.env.MAVEN_PASSWORD;
if (!username || !password) {
console.error('❌ Missing MAVEN_USERNAME or MAVEN_PASSWORD');
process.exit(1);
}
if (!deploymentZipPath) {
console.error('❌ Missing required --deploymentZipPath argument');
process.exit(1);
}
await publishToMavenApi(username, password, deploymentZipPath);
})();