Skip to content

Commit

Permalink
windows publish sample: publish draft release via nodejs
Browse files Browse the repository at this point in the history
  • Loading branch information
boly38 committed Dec 3, 2024
1 parent db5dbfd commit 1080353
Show file tree
Hide file tree
Showing 11 changed files with 639 additions and 57 deletions.
15 changes: 15 additions & 0 deletions .github/gha_1_check_version_data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {checkGithubVersionAndExport, exportMatchingVersionData, readPackageVersion} from "./jsLib/versionHelper.js";
import {produceFakeFilename} from "./jsLib/packagingHelper.js";
import {postToGitHubDispatch, waitForCompletion} from "./jsLib/githubApiHelper.js";

/**
* requirements:
* (file)
* ./versions.yml
* (env)
* GITHUB_REF_NAME : provided by GHA
*/

const version = readPackageVersion();
const envVersion = checkGithubVersionAndExport(version);
exportMatchingVersionData(envVersion);
20 changes: 20 additions & 0 deletions .github/gha_3_publish_version.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import {checkGithubVersionAndExport, exportMatchingVersionData, readPackageVersion} from "./jsLib/versionHelper.js";
import {produceFakeFilename} from "./jsLib/packagingHelper.js";
import {postToGitHubDispatch, waitForCompletion} from "./jsLib/githubApiHelper.js";

/**
* requirements:
* (env)
* env.VERSION
* env.LABEL
* env.LABEL_FR
* env.DESCRIPTION
* env.DESCRIPTION_FR
* RELEASE_EXE_FILE
* REPO_API_URL,
* REPO_WEB_URL
* PAT_TOKEN
*/

await postToGitHubDispatch(process.env.VERSION)
await waitForCompletion()
72 changes: 72 additions & 0 deletions .github/gha_4_create_release_draft.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import fetch from 'node-fetch';
import fs from 'fs';

async function createRelease() {
const {
GITHUB_ENV,
GITHUB_REF_NAME,
GITHUB_TOKEN,
REPO_API_URL,
LABEL,
LABEL_FR,
DESCRIPTION,
DESCRIPTION_FR,
} = process.env;

if (!GITHUB_TOKEN || !REPO_API_URL || !GITHUB_REF_NAME || !LABEL || !DESCRIPTION || !GITHUB_ENV) {
throw new Error('Missing required environment variables.');
}

const requestBody = {
tag_name: GITHUB_REF_NAME,
target_commitish: 'main',
name: LABEL,
body: `${DESCRIPTION}\n\n# ${LABEL_FR}\n\n${DESCRIPTION_FR}`,
draft: true,
prerelease: false,
};

try {
// Create release
const response = await fetch(`${REPO_API_URL}/releases`, {
method: 'POST',
headers: {
Authorization: `token ${GITHUB_TOKEN}`,
'Content-Type': 'application/json',
'User-Agent': 'Node.js/Fetch',
},
body: JSON.stringify(requestBody),
});

if (!response.ok) {
const errorText = await response.text();
throw new Error(`Failed to create release: ${errorText}`);
}

const responseData = await response.json();

// Write API response to file
const responseFile = 'release_response.json';
fs.writeFileSync(responseFile, JSON.stringify(responseData, null, 2));
console.log(`Release creation response saved to: ${responseFile}`);

const releaseId = responseData.id;
const uploadUrl = responseData.upload_url;

if (!releaseId) {
throw new Error(`Release ID is null. Full response: ${JSON.stringify(responseData)}`);
}

console.log(`Release created successfully! id: ${releaseId} - upload_url: ${uploadUrl}`);

// Export variables to GitHub Actions environment
fs.appendFileSync(GITHUB_ENV, `RELEASE_ID=${releaseId}\n`);
fs.appendFileSync(GITHUB_ENV, `RELEASE_UPLOAD_URL=${uploadUrl}\n`);

} catch (error) {
console.error('Error creating release:', error.message);
process.exit(1);
}
}

createRelease();
186 changes: 186 additions & 0 deletions .github/jsLib/githubApiHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
import https from 'https';

export const postToGitHubDispatch = async () => {
const {
PAT_TOKEN,
REPO_API_URL,
VERSION,
LABEL,
LABEL_FR,
DESCRIPTION,
DESCRIPTION_FR,
REPO_WEB_URL
} = process.env;

if (!PAT_TOKEN || !REPO_API_URL || !VERSION) {
console.error('🔴 Error: Missing required environment variables.');
process.exit(1);
}

const data = JSON.stringify({
event_type: 'push-new-version',
client_payload: {
version: VERSION,
label: LABEL,
label_fr: LABEL_FR,
description: DESCRIPTION,
description_fr: DESCRIPTION_FR,
note: `${REPO_WEB_URL}/releases/tag/v${VERSION}`,
download: `${REPO_WEB_URL}/releases/download/v${VERSION}/chicken-bot.exe`,
},
});

const options = {
hostname: new URL(REPO_API_URL).hostname,
path: new URL(REPO_API_URL).pathname + '/dispatches',
method: 'POST',
headers: {
'Authorization': `token ${PAT_TOKEN}`,
'User-Agent': 'GitHub-Actions-NodeJS-Script',
'Accept': 'application/vnd.github.everest-preview+json',
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(data),
},
};

const sendRequest = () =>
new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
let responseBody = '';
res.on('data', (chunk) => {
responseBody += chunk;
});
res.on('end', () => {
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve(responseBody);
} else {
reject(
new Error(
`Request failed with status ${res.statusCode}: ${responseBody}`
)
);
}
});
});

req.on('error', (err) => reject(err));
req.write(data);
req.end();
});

try {
const response = await sendRequest();
console.log('✅ Successfully dispatched event to GitHub:');
console.log(response);
} catch (error) {
console.error(`🔴 Error: ${error.message}`);
process.exit(1);
}
};

export const timeout = ms => {
return new Promise(resolve => setTimeout(resolve, ms));
}

const fetch = (url, token) =>
new Promise((resolve, reject) => {
const options = {
method: 'GET',
headers: {
Authorization: `token ${token}`,
'User-Agent': 'GitHub-Actions-NodeJS-Script'
},
};

const req = https.request(url, options, (res) => {
let data = '';

res.on('data', (chunk) => {
data += chunk;
});

res.on('end', () => {
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve(JSON.parse(data));
} else {
reject(new Error(`HTTP ${res.statusCode}: ${data}`));
}
});
});

req.on('error', (err) => reject(err));
req.end();
});

export const waitForCompletion = async () => {
const {
PAT_TOKEN,
REPO_API_URL,
} = process.env;

if (!PAT_TOKEN || !REPO_API_URL) {
console.error('🔴 Error: Missing required environment variables.');
process.exit(1);
}

const workflowName = 'push-new-version';
let runId = null;

console.log(`ℹ️ Waiting for workflow "${workflowName}" to start...`);

// Poll for the workflow run ID
while (!runId) {
try {
const response = await fetch(
`${REPO_API_URL}/actions/runs?event=repository_dispatch&status=in_progress`,
PAT_TOKEN
);

const workflowRuns = response.workflow_runs || [];
const matchingRun = workflowRuns.find(
(run) => run.display_title === workflowName
);

if (matchingRun) {
runId = matchingRun.id;
} else {
console.log('Waiting for workflow run ID...');
await timeout(3000);
}
} catch (error) {
console.error(`🔴 Error fetching workflow runs: ${error.message}`);
process.exit(1);
}
}

console.log(`ℹ️ Workflow ID: ${runId}`);

// Poll for the workflow completion status
let conclusion = null;
while (!conclusion || conclusion === 'in_progress') {
try {
const response = await fetch(
`${REPO_API_URL}/actions/runs/${runId}`,
PAT_TOKEN
);

conclusion = response.conclusion;
if (conclusion === 'success') {
console.log('✅ Workflow completed successfully.');
break;
} else if (conclusion === 'failure') {
console.error('🔴 The workflow FAILED.');
process.exit(1);
} else if (conclusion === 'cancelled') {
console.error('🚫 The workflow was CANCELLED.');
process.exit(1);
} else {
console.log('Waiting for the workflow to complete...');
await timeout(10000);
}
} catch (error) {
console.error(`🔴 Error fetching workflow status: ${error.message}`);
process.exit(1);
}
}
};
38 changes: 38 additions & 0 deletions .github/jsLib/packagingHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import fs from 'fs';
import path from 'path';

export const produceFakeFilename = async (sizeMb = 82) => {
try {
const fileName = process.env.FILE_NAME;

if (!fileName) {
console.error('🔴 Error: FILE_NAME environment variable is not set.');
process.exit(1);
}

// Create the ./packages directory if it doesn't exist
const packagesDir = path.resolve('./packages');
if (!fs.existsSync(packagesDir)) {
fs.mkdirSync(packagesDir, {recursive: true});
}

// Generate a random file of sizeMb MB
const filePath = path.join(packagesDir, fileName);
const randomData = Buffer.alloc(sizeMb * 1024 * 1024); // 82 MB buffer
fs.writeFileSync(filePath, randomData);

console.log(`Preparing release file ${filePath} done:`);

// List files in ./packages
const files = fs.readdirSync(packagesDir).map(file => {
const stats = fs.statSync(path.join(packagesDir, file));
return `${file} - ${stats.size} bytes`;
});

console.log('Files in ./packages:');
console.log(files.join('\n'));
} catch (error) {
console.error(`🔴 Error: ${error.message}`);
process.exit(1);
}
};
Loading

0 comments on commit 1080353

Please sign in to comment.