mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-02-03 08:58:07 +00:00
Improves the artifact comment workflow to provide better visibility into ongoing builds by: - Triggering comments when builds start (requested event) instead of only on completion - Using commit SHA for concurrency grouping to better handle multiple builds for the same commit - Collecting artifacts from all recent build workflows for a PR rather than just the current run - Adding a build status table showing progress of Android and iOS workflows - Displaying progressive status updates even when builds are still in progress - Enabling cancel-in-progress to prevent redundant workflow runs This provides users with immediate feedback on build progress and comprehensive artifact availability across all platforms.
217 lines
9.4 KiB
YAML
217 lines
9.4 KiB
YAML
name: 📝 Artifact Comment on PR
|
|
|
|
concurrency:
|
|
group: artifact-comment-${{ github.event.workflow_run.head_sha || github.sha }}
|
|
cancel-in-progress: true
|
|
|
|
on:
|
|
workflow_dispatch: # Allow manual testing
|
|
workflow_run:
|
|
workflows:
|
|
- "🤖 Android APK Build (Phone + TV)"
|
|
- "🤖 iOS IPA Build (Phone + TV)"
|
|
types:
|
|
- completed
|
|
- requested # Trigger when build starts
|
|
|
|
jobs:
|
|
comment-artifacts:
|
|
if: github.event_name == 'workflow_dispatch' || github.event.workflow_run.event == 'pull_request'
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
pull-requests: write
|
|
actions: read
|
|
|
|
steps:
|
|
- name: 🔍 Get PR and Artifacts
|
|
uses: actions/github-script@v8
|
|
with:
|
|
script: |
|
|
// Handle both workflow_run and manual dispatch events
|
|
let pr;
|
|
|
|
if (context.eventName === 'workflow_run') {
|
|
// Find PR associated with this commit
|
|
const { data: pullRequests } = await github.rest.repos.listPullRequestsAssociatedWithCommit({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
commit_sha: github.event.workflow_run.head_sha
|
|
});
|
|
|
|
if (pullRequests.length === 0) {
|
|
console.log('No pull request found for commit:', github.event.workflow_run.head_sha);
|
|
return;
|
|
}
|
|
pr = pullRequests[0];
|
|
|
|
} else if (context.eventName === 'workflow_dispatch') {
|
|
// Get current PR for manual testing
|
|
const prNumber = context.payload.pull_request?.number || 1101;
|
|
const { data: prData } = await github.rest.pulls.get({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
pull_number: prNumber
|
|
});
|
|
pr = prData;
|
|
|
|
} else {
|
|
console.log('Unsupported event type:', context.eventName);
|
|
return;
|
|
}
|
|
|
|
console.log(`Processing PR #${pr.number} for commit ${pr.head.sha.substring(0, 7)}`);
|
|
|
|
// Get all recent workflow runs for this PR to collect artifacts from multiple builds
|
|
const { data: workflowRuns } = await github.rest.actions.listWorkflowRuns({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
head_sha: pr.head.sha,
|
|
per_page: 10
|
|
});
|
|
|
|
// Filter for build workflows only
|
|
const buildRuns = workflowRuns.workflow_runs.filter(run =>
|
|
run.name.includes('Android APK Build') ||
|
|
run.name.includes('iOS IPA Build')
|
|
);
|
|
|
|
console.log(`Found ${buildRuns.length} build workflow runs for this commit`);
|
|
|
|
// Collect artifacts from all completed successful builds
|
|
let allArtifacts = [];
|
|
let buildStatuses = {};
|
|
|
|
for (const run of buildRuns) {
|
|
buildStatuses[run.name] = {
|
|
status: run.status,
|
|
conclusion: run.conclusion,
|
|
url: run.html_url,
|
|
runId: run.id
|
|
};
|
|
|
|
if (run.conclusion === 'success') {
|
|
try {
|
|
const { data: artifacts } = await github.rest.actions.listWorkflowRunArtifacts({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
run_id: run.id
|
|
});
|
|
allArtifacts.push(...artifacts.artifacts);
|
|
} catch (error) {
|
|
console.log(`Failed to get artifacts for run ${run.id}:`, error.message);
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log(`Collected ${allArtifacts.length} total artifacts from all builds`);
|
|
|
|
// Get artifacts from current run if needed
|
|
const { data: artifacts } = await github.rest.actions.listWorkflowRunArtifacts({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
run_id: runId
|
|
});
|
|
|
|
// Sort and categorize all collected artifacts
|
|
const androidArtifacts = allArtifacts
|
|
.filter(a => a.name.includes('android'))
|
|
.sort((a, b) => a.name.localeCompare(b.name));
|
|
const iosArtifacts = allArtifacts
|
|
.filter(a => a.name.includes('ios'))
|
|
.sort((a, b) => a.name.localeCompare(b.name));
|
|
|
|
// Build comment body with progressive status
|
|
let commentBody = `## 📱 Build Status for PR #${pr.number}\n\n`;
|
|
commentBody += `🔗 **Commit**: [\`${pr.head.sha.substring(0, 7)}\`](https://github.com/${context.repo.owner}/${context.repo.repo}/commit/${pr.head.sha})\n\n`;
|
|
|
|
// Add build status table
|
|
commentBody += `### 🔧 Build Status\n\n`;
|
|
commentBody += `| Workflow | Status | Artifacts |\n`;
|
|
commentBody += `|----------|--------|-----------|\n`;
|
|
|
|
for (const [name, status] of Object.entries(buildStatuses)) {
|
|
const emoji = status.conclusion === 'success' ? '✅' :
|
|
status.conclusion === 'failure' ? '❌' :
|
|
status.status === 'in_progress' ? '🔄' : '⏳';
|
|
const statusText = status.conclusion || status.status || 'pending';
|
|
const artifactCount = allArtifacts.filter(a => {
|
|
// Match artifacts to workflows based on naming patterns
|
|
if (name.includes('Android')) return a.name.includes('android');
|
|
if (name.includes('iOS')) return a.name.includes('ios');
|
|
return false;
|
|
}).length;
|
|
|
|
commentBody += `| [${name}](${status.url}) | ${emoji} ${statusText} | ${artifactCount} |\n`;
|
|
}
|
|
|
|
commentBody += `\n`;
|
|
|
|
// Only show download table if there are artifacts
|
|
if (allArtifacts.length > 0) {
|
|
commentBody += `### 📦 Available Downloads (${allArtifacts.length} artifacts)\n\n`;
|
|
|
|
// Create table for better organization
|
|
commentBody += `| Platform | Device Type | Download Link |\n`;
|
|
commentBody += `|----------|-------------|---------------|\n`;
|
|
|
|
// Add Android artifacts
|
|
androidArtifacts.forEach(artifact => {
|
|
const isTV = artifact.name.includes('tv');
|
|
const deviceType = isTV ? '📺 Android TV' : '📱 Android Phone';
|
|
const nightlyLink = `https://nightly.link/${context.repo.owner}/${context.repo.repo}/actions/runs/${artifact.workflow_run.id}/${artifact.name}.zip`;
|
|
commentBody += `| 🤖 Android | ${deviceType} | [📥 Download APK](${nightlyLink}) |\n`;
|
|
});
|
|
|
|
// Add iOS artifacts
|
|
iosArtifacts.forEach(artifact => {
|
|
const isTV = artifact.name.includes('tv');
|
|
const deviceType = isTV ? '📺 Apple TV' : '📱 iPhone/iPad';
|
|
const nightlyLink = `https://nightly.link/${context.repo.owner}/${context.repo.repo}/actions/runs/${artifact.workflow_run.id}/${artifact.name}.zip`;
|
|
commentBody += `| 🍎 iOS | ${deviceType} | [📥 Download IPA](${nightlyLink}) |\n`;
|
|
});
|
|
|
|
commentBody += `\n`;
|
|
commentBody += `### 🔧 Installation Instructions\n\n`;
|
|
commentBody += `- **Android APK**: Download and install directly on your device (enable "Install from unknown sources")\n`;
|
|
commentBody += `- **iOS IPA**: Install using [AltStore](https://altstore.io/), [Sideloadly](https://sideloadly.io/), or Xcode\n\n`;
|
|
commentBody += `> ⚠️ **Note**: Artifacts expire in 7 days from build date\n\n`;
|
|
} else {
|
|
commentBody += `⏳ **No artifacts available yet** - builds are still in progress or haven't completed successfully.\n\n`;
|
|
}
|
|
|
|
commentBody += `<sub>*Auto-generated by [GitHub Actions](https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId})*</sub>`;
|
|
commentBody += `\n<!-- streamyfin-artifact-comment -->`;
|
|
|
|
// Find existing bot comment to update
|
|
const { data: comments } = await github.rest.issues.listComments({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number: pr.number
|
|
});
|
|
|
|
const botComment = comments.find(comment =>
|
|
comment.user.type === 'Bot' &&
|
|
comment.body.includes('<!-- streamyfin-artifact-comment -->')
|
|
);
|
|
|
|
if (botComment) {
|
|
// Update existing comment
|
|
await github.rest.issues.updateComment({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
comment_id: botComment.id,
|
|
body: commentBody
|
|
});
|
|
console.log(`✅ Updated comment ${botComment.id} on PR #${pr.number}`);
|
|
} else {
|
|
// Create new comment
|
|
await github.rest.issues.createComment({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number: pr.number,
|
|
body: commentBody
|
|
});
|
|
console.log(`✅ Created new comment on PR #${pr.number}`);
|
|
}
|