chore: streamline release workflows by adding version bump and built files commit step; remove redundant asset upload steps

This commit is contained in:
Luke Hagar
2025-08-14 22:29:38 -05:00
parent 1b694ebe63
commit 5cf3629bc8
14 changed files with 2031 additions and 107 deletions

268
dist/collectors/github.js vendored Normal file
View File

@@ -0,0 +1,268 @@
/**
* GitHub repository statistics collector with enhanced metrics using Octokit SDK and GraphQL
*/
import { Octokit } from '@octokit/rest';
import { graphql } from '@octokit/graphql';
const PlatformSettings = {
name: 'GitHub',
};
// GraphQL query for basic repository data (without releases)
const REPOSITORY_BASIC_QUERY = `
query RepositoryBasicData($owner: String!, $name: String!) {
repository(owner: $owner, name: $name) {
id
name
description
homepageUrl
stargazerCount
forkCount
watchers {
totalCount
}
openIssues: issues(states: OPEN) {
totalCount
}
closedIssues: issues(states: CLOSED) {
totalCount
}
primaryLanguage {
name
}
diskUsage
createdAt
updatedAt
pushedAt
defaultBranchRef {
name
}
repositoryTopics(first: 10) {
nodes {
topic {
name
}
}
}
licenseInfo {
name
spdxId
}
}
}
`;
// GraphQL query for releases with download data
const RELEASES_QUERY = `
query RepositoryReleases($owner: String!, $name: String!, $first: Int!) {
repository(owner: $owner, name: $name) {
releases(first: $first, orderBy: {field: CREATED_AT, direction: DESC}) {
nodes {
id
tagName
name
description
createdAt
publishedAt
releaseAssets(first: 100) {
nodes {
id
name
size
downloadCount
downloadUrl
}
}
}
}
}
}
`;
export async function collectGithub(repository) {
try {
const [owner, repo] = repository.split('/');
if (!owner || !repo) {
throw new Error(`Invalid repository format: ${repository}. Expected "owner/repo"`);
}
// Initialize Octokit for REST API calls
const token = process.env.GITHUB_TOKEN || process.env.INPUT_GITHUB_TOKEN || '';
const octokit = new Octokit({
auth: token,
userAgent: 'usage-statistics-tracker'
});
if (!token) {
console.warn('No GitHub token provided. Using unauthenticated requests (rate limited).');
}
// Step 1: Fetch basic repository data using GraphQL
let graphqlData = null;
try {
const graphqlClient = graphql.defaults({
headers: {
authorization: token ? `token ${token}` : undefined,
},
});
// Fetch basic repository data (without releases)
const basicResponse = await graphqlClient(REPOSITORY_BASIC_QUERY, {
owner,
name: repo
});
if (basicResponse.repository) {
graphqlData = basicResponse.repository;
}
}
catch (error) {
console.warn(`Could not fetch GitHub GraphQL basic data for ${repository}:`, error);
}
// Step 2: Fetch releases data separately using GraphQL
let totalReleaseDownloads = 0;
let latestReleaseDownloads = 0;
let releaseCount = 0;
let downloadRange = [];
let latestRelease = null;
try {
const graphqlClient = graphql.defaults({
headers: {
authorization: token ? `token ${token}` : undefined,
},
});
// Fetch releases data
const releasesResponse = await graphqlClient(RELEASES_QUERY, {
owner,
name: repo,
first: 100
});
if (releasesResponse.repository?.releases?.nodes) {
const releases = releasesResponse.repository.releases.nodes.filter(Boolean);
releaseCount = releases.length;
for (const release of releases) {
let releaseDownloads = 0;
if (release?.releaseAssets?.nodes) {
for (const asset of release.releaseAssets.nodes) {
if (asset) {
releaseDownloads += asset.downloadCount || 0;
}
}
}
totalReleaseDownloads += releaseDownloads;
// Latest release is the first one in the list
if (release && release === releases[0]) {
latestReleaseDownloads = releaseDownloads;
latestRelease = release.tagName;
}
// Add to download range with proper date format for charts
if (release?.publishedAt) {
downloadRange.push({
day: release.publishedAt,
downloads: releaseDownloads,
tagName: release.tagName
});
}
}
}
}
catch (error) {
console.warn(`Could not fetch GitHub GraphQL releases data for ${repository}:`, error);
}
// Fallback to REST API if GraphQL fails or for additional data
let restData = null;
try {
const { data: repoData } = await octokit.repos.get({
owner,
repo
});
restData = repoData;
}
catch (error) {
console.warn(`Could not fetch GitHub REST data for ${repository}:`, error);
}
// Use the best available data (GraphQL preferred, REST as fallback)
const finalData = graphqlData || restData;
if (!finalData) {
throw new Error('Could not fetch repository data from either GraphQL or REST API');
}
// Get traffic statistics using REST API (requires authentication)
let viewsCount = 0;
let uniqueVisitors = 0;
let clonesCount = 0;
if (token) {
try {
// Get views data
const { data: viewsData } = await octokit.repos.getViews({
owner,
repo
});
if (viewsData) {
viewsCount = viewsData.count || 0;
uniqueVisitors = viewsData.uniques || 0;
}
// Get clones data
const { data: clonesData } = await octokit.repos.getClones({
owner,
repo
});
if (clonesData) {
clonesCount = clonesData.count || 0;
}
}
catch (error) {
console.warn(`Could not fetch GitHub traffic data for ${repository}:`, error);
}
}
// Calculate repository age
let repositoryAge = 0;
if (finalData.createdAt) {
const created = new Date(finalData.createdAt);
const now = new Date();
repositoryAge = Math.floor((now.getTime() - created.getTime()) / (1000 * 60 * 60 * 24)); // days
}
// Calculate activity metrics
let lastActivity = 0;
if (finalData.pushedAt) {
const pushed = new Date(finalData.pushedAt);
const now = new Date();
lastActivity = Math.floor((now.getTime() - pushed.getTime()) / (1000 * 60 * 60 * 24)); // days
}
return {
platform: PlatformSettings.name,
name: repository,
timestamp: new Date().toISOString(),
metrics: {
stars: finalData.stargazerCount || finalData.stargazers_count || 0,
forks: finalData.forkCount || finalData.forks_count || 0,
watchers: finalData.watchers?.totalCount || finalData.watchers_count || 0,
totalIssues: finalData.openIssues?.totalCount + finalData.closedIssues?.totalCount || 0,
openIssues: finalData.openIssues?.totalCount || 0,
closedIssues: finalData.closedIssues?.totalCount || 0,
language: finalData.primaryLanguage?.name || finalData.language || null,
size: finalData.diskUsage || finalData.size || null,
repositoryAge,
lastActivity,
releaseCount,
totalReleaseDownloads,
latestReleaseDownloads,
viewsCount,
uniqueVisitors,
latestRelease,
clonesCount,
topics: finalData.repositoryTopics?.nodes?.length || finalData.topics?.length || 0,
license: finalData.licenseInfo?.name || finalData.license?.name || null,
defaultBranch: finalData.defaultBranchRef?.name || finalData.default_branch || null,
downloadsTotal: totalReleaseDownloads || 0,
downloadRange,
}
};
}
catch (error) {
return {
platform: PlatformSettings.name,
name: repository,
timestamp: new Date().toISOString(),
metrics: {},
error: error instanceof Error ? error.message : String(error)
};
}
}
export async function collectGithubBatch(repositories) {
const results = [];
for (const repo of repositories) {
results.push(collectGithub(repo));
}
return Promise.all(results);
}