Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve webui and experimental extension #500

Merged
merged 1 commit into from
Jan 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,23 @@
"requireExactSource": false
},
{
"name": "Run azure-pipelines-vscode-ext Extension",
"name": "Run azure-pipelines-vscode-ext Extension",
"type": "extensionHost",
"debugWebWorkerHost": true,
"request": "launch",
"runtimeExecutable": "${execPath}",
"request": "launch",
"runtimeExecutable": "${execPath}",
"cwd": "${workspaceFolder}/src/azure-pipelines-vscode-ext",
"args": ["--extensionDevelopmentPath=${workspaceFolder}/src/azure-pipelines-vscode-ext"]
}
"args": ["--extensionDevelopmentPath=${workspaceFolder}/src/azure-pipelines-vscode-ext"]
},
{
"name": "Run runner-server-vscode Extension",
"type": "extensionHost",
"debugWebWorkerHost": true,
"request": "launch",
"runtimeExecutable": "${execPath}",
"cwd": "${workspaceFolder}/src/runner-server-vscode",
"args": ["--extensionDevelopmentPath=${workspaceFolder}/src/runner-server-vscode"]
}
],
}

34 changes: 25 additions & 9 deletions src/Runner.Server/actions-service-webapp/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import remarkGfm from 'remark-gfm'
import remarkBreaks from 'remark-breaks'
import rehypeRaw from 'rehype-raw';
import rehypeHighlight from 'rehype-highlight';
import { CircleIcon, SkipIcon, StopIcon, XCircleFillIcon, CheckCircleFillIcon, ChevronDownIcon, ChevronRightIcon, GitCommitIcon, RepoIcon, PersonIcon, MeterIcon } from '@primer/octicons-react'
import { CircleIcon, SkipIcon, StopIcon, XCircleFillIcon, CheckCircleFillIcon, ChevronDownIcon, ChevronRightIcon, GitCommitIcon, RepoIcon, PersonIcon, MeterIcon, ClockIcon, ClockFillIcon } from '@primer/octicons-react'
import { ghHostApiUrl } from './config';

var convert = new Convert({
Expand Down Expand Up @@ -105,7 +105,7 @@ function List({ fullscreen } : { fullscreen?: boolean }) {
<Link className='btn btn-primary w-50' to={"../"+ (page + 1) + (fullscreen ? "" : "/" + params['*'])}>Next</Link>
</div>
{jobs.map(val => (
<NavLink key={val.jobId} to={encodeURIComponent(val.jobId)} className={({isActive})=> isActive ? 'btn btn-outline-secondary w-100 text-start active' : 'btn btn-outline-secondary w-100 text-start'}><span style={{fontSize: 20}}>{val.name}</span><br/><span style={{fontSize: 12}}>{!params.runid ? (<>repo:&nbsp;{val.repo} workflow:&nbsp;{val.workflowname} runid:&nbsp;{val.runid} </>) : (<></>)}attempt:&nbsp;{val.attempt} result:&nbsp;<TimelineStatus status={val.result ?? "inprogress"}/></span></NavLink>
<NavLink key={val.jobId} to={encodeURIComponent(val.jobId)} className={({isActive})=> isActive ? 'btn btn-outline-secondary w-100 text-start active' : 'btn btn-outline-secondary w-100 text-start'}><span style={{fontSize: 20}}>{val.name}</span><br/><span style={{fontSize: 12}}>{!params.runid ? (<>repo:&nbsp;{val.repo} workflow:&nbsp;{val.workflowname} runid:&nbsp;{val.runid} </>) : (<></>)}attempt:&nbsp;{val.attempt} result:&nbsp;<TimelineStatus status={val.result ?? (val.sessionId === '00000000-0000-0000-0000-000000000000' ? "queued" : "inprogress")}/></span></NavLink>
))}
{ loading ?
<div className="spinner-border" role="status">
Expand Down Expand Up @@ -383,7 +383,8 @@ export interface IJob {
runid : number,
errors: string[],
result: string,
attempt: number
attempt: number,
sessionId: string
}

interface ChunkProps {
Expand Down Expand Up @@ -547,9 +548,11 @@ interface IWorkflowRunAttempt {

const TimelineStatus = ({status, size} : { status : string, size?: number }) => {
switch(status?.toLowerCase()) {
case "running":
case "inprogress":
return <MeterIcon className="text-warning progress-ring" size={size}/>
case "waiting":
return <ClockFillIcon className="text-warning" verticalAlign="middle" size={size}/>
case "pending":
return <CircleIcon verticalAlign="middle" size={size}/>
case "succeeded":
Expand All @@ -560,6 +563,8 @@ const TimelineStatus = ({status, size} : { status : string, size?: number }) =>
return <SkipIcon verticalAlign="middle" size={size}/>
case "canceled":
return <StopIcon verticalAlign="middle" size={size}/>
case "queued":
return <CircleIcon className="text-warning" verticalAlign="middle" size={size}/>
default:
return <span>{status}</span>
}
Expand Down Expand Up @@ -598,6 +603,7 @@ interface IWorkflowRun {
ref: string,
sha: string,
result: string
status: string
}
function JobPage() {
var params = useParams();
Expand Down Expand Up @@ -735,9 +741,13 @@ function JobPage() {
})();
return () => signal.abort();
}, [artifacts]);
return ( <span style={{width: '100%', height: '100%', overflowY: 'auto'}}>
<h1>{workflowRun ? workflowRun.fileName : job ? (<><TimelineStatus status={job?.result ?? "inprogress"} size={32}/> {job.name}</>) : ""}</h1>
var searchParams = new URLSearchParams(window.location.search)
return ( <span style={{width: '100%', height: '100%', overflowY: 'auto'}}>
<h1>{workflowRun ? workflowRun.fileName : job ? (<><TimelineStatus status={job?.result ?? (job.sessionId === '00000000-0000-0000-0000-000000000000' ? "queued" : "inprogress")} size={32}/> {job.name}</>) : ""}</h1>
{(() => {
if(searchParams.get("extension") === "1") {
return <></>
}
if(job !== undefined && job != null) {
if(!job.result && (!job.errors || job.errors.length === 0)) {
return <div className="btn-group" role="group">
Expand Down Expand Up @@ -861,6 +871,7 @@ interface IRepository {
interface IWorkflowRun {
id: string,
fileName: string
status: string
}

function RedirectOldUrl() {
Expand Down Expand Up @@ -967,21 +978,26 @@ function App() {
( <AllJobs/> ) :
searchParams.get("view") === "allworkflows" ?
( <Routes>
<Route path=":page" element={<GenericList hasBack={false} id={(o: IWorkflowRun) => o.id} summary={(o: IWorkflowRun) => <span>{o.displayName ?? o.fileName}<br/>{ o.owner && o.repo ? <>Repository: {o.owner}/{o.repo} </> : <></>}RunId: {o.id}, EventName: {o.eventName}<br/>Workflow: {o.fileName}<br/>{o.ref} {o.sha} <TimelineStatus status={o.result ?? "Pending"}/></span>} url={(params) => `${ghHostApiUrl}/_apis/v1/Message/workflow/runs?page=${params.page || "0"}`} eventName="workflowrun" eventUpdateName="workflowrunupdate" eventQuery={ params => `owner=${encodeURIComponent(params.owner || "")}&repo=${encodeURIComponent(params.repo || "")}` }></GenericList>}/>
<Route path=":page" element={<GenericList hasBack={false} id={(o: IWorkflowRun) => o.id} summary={(o: IWorkflowRun) => <span>{o.displayName ?? o.fileName}<br/>{ o.owner && o.repo ? <>Repository: {o.owner}/{o.repo} </> : <></>}RunId: {o.id}, EventName: {o.eventName}<br/>Workflow: {o.fileName}<br/>{o.ref} {o.sha} <TimelineStatus status={o.result ?? o.status ?? "Pending"}/></span>} url={(params) => `${ghHostApiUrl}/_apis/v1/Message/workflow/runs?page=${params.page || "0"}`} eventName="workflowrun" eventUpdateName="workflowrunupdate" eventQuery={ params => `owner=${encodeURIComponent(params.owner || "")}&repo=${encodeURIComponent(params.repo || "")}` }></GenericList>}/>
<Route path="/" element={<Navigate to={"0"}/>}/>
<Route path=":page/:runid/*" element={
<div style={{display: 'flex', flexFlow: 'row', alignItems: 'left', width: '100%', height: '100%'}}>
<Routes>
{searchParams.get("extension") === "1" ? <></> : (<Routes>
<Route path=":page/*" element={<List/>}/>
<Route path="/" element={<Navigate to={"0"}/>}/>
</Routes>
</Routes>)}
<Routes>
<Route path=":page/:id/*" element={<JobPage></JobPage>}/>
<Route path=":page" element={<JobPage></JobPage>}/>
</Routes>
</div>
}/>
</Routes> ) :
searchParams.get("view") === "singlejob" ? (
<Routes>
<Route path=":id/*" element={<JobPage></JobPage>}/>
</Routes>
) :
(
<Routes>
<Route path="/timeline/:timeLineId" element={<TimeLineViewer></TimeLineViewer>}/>
Expand All @@ -995,7 +1011,7 @@ function App() {
<Route path="/" element={<Navigate to={"0"}/>}/>
<Route path=":page/:repo/*" element={
<Routes>
<Route path=":page" element={<GenericList id={(o: IWorkflowRun) => o.id} hasBack={true} summary={(o: IWorkflowRun) => <span>{o.displayName ?? o.fileName}<br/>RunId: {o.id}, EventName: {o.eventName}<br/>Workflow: {o.fileName}<br/>{o.ref} {o.sha} <TimelineStatus status={o.result ?? "Pending"}/></span>} url={(params) => `${ghHostApiUrl}/_apis/v1/Message/workflow/runs?owner=${encodeURIComponent(params.owner || "")}&repo=${encodeURIComponent(params.repo || "")}&page=${params.page || "0"}`} externalBackUrl={params => gitServerUrl && new URL(`${params.owner}/${params.repo}`, gitServerUrl).href} externalBackLabel={() => "Back to git"} actions={ (run, params) => gitServerUrl ? <a className='btn btn-outline-secondary' href={new URL(`${params.owner}/${params.repo}/commit/${run.sha}`, gitServerUrl).href} target="_blank" rel="noreferrer"><GitCommitIcon verticalAlign='middle' size={24}/></a> : <></> } eventName="workflowrun" eventUpdateName="workflowrunupdate" eventQuery={ params => `owner=${encodeURIComponent(params.owner || "")}&repo=${encodeURIComponent(params.repo || "")}` }></GenericList>}/>
<Route path=":page" element={<GenericList id={(o: IWorkflowRun) => o.id} hasBack={true} summary={(o: IWorkflowRun) => <span>{o.displayName ?? o.fileName}<br/>RunId: {o.id}, EventName: {o.eventName}<br/>Workflow: {o.fileName}<br/>{o.ref} {o.sha} <TimelineStatus status={o.result ?? o.status ?? "Pending"}/></span>} url={(params) => `${ghHostApiUrl}/_apis/v1/Message/workflow/runs?owner=${encodeURIComponent(params.owner || "")}&repo=${encodeURIComponent(params.repo || "")}&page=${params.page || "0"}`} externalBackUrl={params => gitServerUrl && new URL(`${params.owner}/${params.repo}`, gitServerUrl).href} externalBackLabel={() => "Back to git"} actions={ (run, params) => gitServerUrl ? <a className='btn btn-outline-secondary' href={new URL(`${params.owner}/${params.repo}/commit/${run.sha}`, gitServerUrl).href} target="_blank" rel="noreferrer"><GitCommitIcon verticalAlign='middle' size={24}/></a> : <></> } eventName="workflowrun" eventUpdateName="workflowrunupdate" eventQuery={ params => `owner=${encodeURIComponent(params.owner || "")}&repo=${encodeURIComponent(params.repo || "")}` }></GenericList>}/>
<Route path="/" element={<Navigate to={"0"}/>}/>
<Route path=":page/:runid/*" element={
<div style={{display: 'flex', flexFlow: 'row', alignItems: 'left', width: '100%', height: '100%'}}>
Expand Down
6 changes: 3 additions & 3 deletions src/Runner.Server/wwwroot/asset-manifest.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"files": {
"main.css": "/static/css/main.4758f308.css",
"main.js": "/static/js/main.7457c726.js",
"main.js": "/static/js/main.e27ea4d0.js",
"static/js/787.0500ceb2.chunk.js": "/static/js/787.0500ceb2.chunk.js",
"index.html": "/index.html",
"main.4758f308.css.map": "/static/css/main.4758f308.css.map",
"main.7457c726.js.map": "/static/js/main.7457c726.js.map",
"main.e27ea4d0.js.map": "/static/js/main.e27ea4d0.js.map",
"787.0500ceb2.chunk.js.map": "/static/js/787.0500ceb2.chunk.js.map"
},
"entrypoints": [
"static/css/main.4758f308.css",
"static/js/main.7457c726.js"
"static/js/main.e27ea4d0.js"
]
}
2 changes: 1 addition & 1 deletion src/Runner.Server/wwwroot/index.html
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.1.0/github-markdown.css"><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="View workflow runs, jobs and download artifacts"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>Actions Service</title><script defer="defer" src="/static/js/main.7457c726.js"></script><link href="/static/css/main.4758f308.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.1.0/github-markdown.css"><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="View workflow runs, jobs and download artifacts"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>Actions Service</title><script defer="defer" src="/static/js/main.e27ea4d0.js"></script><link href="/static/css/main.4758f308.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
1 change: 0 additions & 1 deletion src/Runner.Server/wwwroot/static/js/main.7457c726.js.map

This file was deleted.

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/Runner.Server/wwwroot/static/js/main.e27ea4d0.js.map

Large diffs are not rendered by default.

40 changes: 36 additions & 4 deletions src/runner-server-vscode/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ function activate(context : ExtensionContext) {
// Custom formatting after the editor has been opened
updateDecorations(editor, logInfo);
})
);
);

(async() => {
console.log("Aquire Dotnet!")
Expand Down Expand Up @@ -185,15 +185,15 @@ function activate(context : ExtensionContext) {
<iframe src="${fullWebServerUri}${url}"></iframe>
</body>
</html>`;
panel.reveal(ViewColumn.One, false);
panel.reveal(ViewColumn.One, true);
};

commands.registerCommand("runner.server.openjob", (runId, id, name) => {
openPanel(name, `?view=allworkflows#/0/${runId}/0/${id}`);
openPanel(name, `?view=allworkflows&extension=1#/0/${runId}/0/${id}`);
});

commands.registerCommand("runner.server.openworkflowrun", runId => {
openPanel(`#${runId}`, `?view=allworkflows#/0/${runId}`);
openPanel(`#${runId}`, `?view=allworkflows&extension=1#/0/${runId}/0`);
});
}
});
Expand Down Expand Up @@ -266,6 +266,38 @@ function activate(context : ExtensionContext) {
});
});

context.subscriptions.push(commands.registerCommand("runner.server.workflow.cancel", async (obj : TreeItem) => {
var items : QuickPickItem[] = [];
if(obj.contextValue?.indexOf("job") !== -1) {
items.push({
label: "Cancel",
description: "Cancel only this job"
});
items.push({
label: "Force Cancel",
description: "Force Cancel only this job"
});
} else {
items.push({
label: "Cancel",
description: "Cancel this workflow run"
});
items.push({
label: "Force Cancel",
description: "Force Cancel this workflow run"
});
}
var selection = await window.showQuickPick(items);
if(!selection) {
return;
}
if(obj.contextValue?.indexOf("job") !== -1) {
await fetch(address + "/_apis/v1/Message/cancel/" + obj.command.arguments[1] + "?force=" + (selection.label.indexOf("Force") !== -1), { method: "POST" });
} else {
await fetch(address + "/_apis/v1/Message/" + (selection.label.indexOf("Force") !== -1 ? "forceCancelWorkflow" : "cancelWorkflow") + "/" + obj.command.arguments[0], { method: "POST" });
}
}));

context.subscriptions.push(client);
client.start();
})();
Expand Down
11 changes: 11 additions & 0 deletions src/runner-server-vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@
"light": "resources/icons/light/logs.svg"
}
},
{
"command": "runner.server.workflow.cancel",
"title": "Cancel",
"shortTitle": "Cancel",
"icon": "$(stop-circle)"
},
{
"command": "runner.server.start-client",
"title": "Start Runner.Client"
Expand Down Expand Up @@ -86,6 +92,11 @@
"command": "runner.server.workflow.logs",
"group": "inline",
"when": "view == workflow-view && viewItem =~ /completed/ && viewItem =~ /job/"
},
{
"command": "runner.server.workflow.cancel",
"group": "inline",
"when": "view == workflow-view && !(viewItem =~ /completed/) && (viewItem =~ /job/ || viewItem =~ /workflow/ )"
}
]
}
Expand Down
Loading