mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-06-28 16:50:29 +01:00
Restructures the artifact comment workflow to display build progress in real-time with individual platform/device status tracking. Changes the status table from workflow-based to target-based (Android Phone/TV, iOS Phone/TV) with dedicated status indicators and download links that update as builds complete. Improves user experience by showing pending builds with appropriate messaging instead of waiting for all builds to finish before displaying any information.
224 lines
9.9 KiB
YAML
224 lines
9.9 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
|
||
pull_request: # Show in PR checks and provide status updates
|
||
types: [opened, synchronize, reopened]
|
||
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_name == 'pull_request' || (github.event_name == 'workflow_run' && github.event.workflow_run.event == 'pull_request')
|
||
name: 📦 Post Build Artifacts
|
||
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 workflow_run, pull_request, 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 === 'pull_request') {
|
||
// Direct PR event
|
||
pr = context.payload.pull_request;
|
||
|
||
} 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.listWorkflowRunsForRepo({
|
||
owner: context.repo.owner,
|
||
repo: context.repo.repo,
|
||
head_sha: pr.head.sha,
|
||
per_page: 20
|
||
});
|
||
|
||
// 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 and statuses from all builds (completed and in-progress)
|
||
let allArtifacts = [];
|
||
let buildStatuses = {};
|
||
|
||
// Define all expected build targets
|
||
const expectedBuilds = {
|
||
'Android Phone': { platform: 'Android', device: 'Phone', emoji: '📱', pattern: 'android.*phone' },
|
||
'Android TV': { platform: 'Android', device: 'TV', emoji: '📺', pattern: 'android.*tv' },
|
||
'iOS Phone': { platform: 'iOS', device: 'Phone', emoji: '📱', pattern: 'ios.*phone' },
|
||
'iOS TV': { platform: 'iOS', device: 'TV', emoji: '📺', pattern: 'ios.*tv' }
|
||
};
|
||
|
||
for (const run of buildRuns) {
|
||
buildStatuses[run.name] = {
|
||
status: run.status,
|
||
conclusion: run.conclusion,
|
||
url: run.html_url,
|
||
runId: run.id
|
||
};
|
||
|
||
// Collect artifacts from any completed successful builds
|
||
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`);
|
||
|
||
// Build comment body with progressive status for individual builds
|
||
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`;
|
||
|
||
// Progressive build status and downloads table
|
||
commentBody += `### <20> Build Artifacts\n\n`;
|
||
commentBody += `| Platform | Device | Status | Download |\n`;
|
||
commentBody += `|----------|--------|--------|---------|\n`;
|
||
|
||
// Process each expected build target individually
|
||
const buildTargets = [
|
||
{ name: 'Android Phone', platform: '🤖', device: '📱', pattern: /android.*phone/i },
|
||
{ name: 'Android TV', platform: '🤖', device: '📺', pattern: /android.*tv/i },
|
||
{ name: 'iOS Phone', platform: '🍎', device: '<27>', pattern: /ios.*phone/i },
|
||
{ name: 'iOS TV', platform: '🍎', device: '📺', pattern: /ios.*tv/i }
|
||
];
|
||
|
||
for (const target of buildTargets) {
|
||
// Find matching workflow run
|
||
const matchingRun = buildRuns.find(run => {
|
||
return (run.name.includes('Android') && target.name.includes('Android')) ||
|
||
(run.name.includes('iOS') && target.name.includes('iOS'));
|
||
});
|
||
|
||
// Find matching artifact
|
||
const matchingArtifact = allArtifacts.find(artifact =>
|
||
target.pattern.test(artifact.name)
|
||
);
|
||
|
||
let status = '⏳ Pending';
|
||
let downloadLink = '*Waiting for build...*';
|
||
|
||
if (matchingRun) {
|
||
if (matchingRun.conclusion === 'success' && matchingArtifact) {
|
||
status = '✅ Complete';
|
||
const nightlyLink = `https://nightly.link/${context.repo.owner}/${context.repo.repo}/actions/runs/${matchingArtifact.workflow_run.id}/${matchingArtifact.name}.zip`;
|
||
const fileType = target.name.includes('Android') ? 'APK' : 'IPA';
|
||
downloadLink = `[📥 Download ${fileType}](${nightlyLink})`;
|
||
} else if (matchingRun.conclusion === 'failure') {
|
||
status = `❌ [Failed](${matchingRun.html_url})`;
|
||
downloadLink = '*Build failed*';
|
||
} else if (matchingRun.status === 'in_progress') {
|
||
status = `🔄 [Building...](${matchingRun.html_url})`;
|
||
downloadLink = '*Build in progress...*';
|
||
} else if (matchingRun.status === 'queued') {
|
||
status = `⏳ [Queued](${matchingRun.html_url})`;
|
||
downloadLink = '*Waiting to start...*';
|
||
}
|
||
}
|
||
|
||
commentBody += `| ${target.platform} ${target.name.split(' ')[0]} | ${target.device} ${target.name.split(' ')[1]} | ${status} | ${downloadLink} |\n`;
|
||
}
|
||
|
||
commentBody += `\n`;
|
||
|
||
// Show installation instructions if we have any artifacts
|
||
if (allArtifacts.length > 0) {
|
||
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 += `⏳ **Builds are starting up...** This comment will update automatically as each build completes.\n\n`;
|
||
}
|
||
|
||
commentBody += `<sub>*Auto-generated by [GitHub Actions](https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.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}`);
|
||
}
|