Support rebuilding a build
Note that like the previous implementation, this actually triggers a build of the project, not a re-build of a given build. Signed-off-by: Andrew Shirley <andrew.shirley@sainsburys.co.uk> Signed-off-by: blam <ben@blam.sh>
This commit is contained in:
@@ -52,8 +52,10 @@ const jobTreeSpec = `actions[*],
|
||||
${lastBuildTreeSpec}
|
||||
jobs{0,1},
|
||||
name,
|
||||
fullName,
|
||||
displayName,
|
||||
fullDisplayName`;
|
||||
fullDisplayName,
|
||||
inQueue`;
|
||||
|
||||
const jobsTreeSpec = `jobs[
|
||||
${jobTreeSpec}
|
||||
@@ -67,8 +69,7 @@ export interface RouterOptions {
|
||||
export async function createRouter(
|
||||
options: RouterOptions,
|
||||
): Promise<express.Router> {
|
||||
// @ts-ignore keeping unused logger for future use
|
||||
const { logger, jenkinsInfoProvider } = options;
|
||||
const { jenkinsInfoProvider } = options;
|
||||
|
||||
const router = Router();
|
||||
router.use(express.json());
|
||||
@@ -169,6 +170,34 @@ export async function createRouter(
|
||||
},
|
||||
);
|
||||
|
||||
router.post(
|
||||
'/v1/entity/:namespace/:kind/:name/job/:jobName/:buildNumber::rebuild',
|
||||
async (request, response) => {
|
||||
const { namespace, kind, name, jobName } = request.params;
|
||||
|
||||
const jenkinsInfo = await jenkinsInfoProvider.getInstance({
|
||||
entityRef: {
|
||||
kind,
|
||||
namespace,
|
||||
name,
|
||||
},
|
||||
jobName,
|
||||
});
|
||||
|
||||
const client = await getClient(jenkinsInfo);
|
||||
|
||||
// looks like the current SDK only supports triggering a new build
|
||||
// can't see any support for replay (re-running the specific build with the same SCM info)
|
||||
|
||||
// Note Jenkins itself has concepts of rebuild and replay on a job.
|
||||
// The latter should be possible to trigger with a POST to /replay/rebuild
|
||||
await client.job.build(jobName);
|
||||
|
||||
// TODO: return the buildNumber which was started.
|
||||
response.send({});
|
||||
},
|
||||
);
|
||||
|
||||
router.use(errorHandler());
|
||||
return router;
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ export interface Project {
|
||||
inQueue: string;
|
||||
// added by us
|
||||
status: string; // == inQueue ? 'queued' : lastBuild.building ? 'running' : lastBuild.result,
|
||||
onRestartClick: () => void; // TODO handle.* ?
|
||||
onRestartClick: () => Promise<void>; // TODO rename to handle.* ? also, should this be on lastBuild?
|
||||
}
|
||||
|
||||
export interface JenkinsApi {
|
||||
@@ -98,7 +98,11 @@ export interface JenkinsApi {
|
||||
buildNumber: string,
|
||||
): Promise<Build>;
|
||||
|
||||
retry(entity: EntityName, jobName: string, buildNumber: string): Promise<any>;
|
||||
retry(
|
||||
entity: EntityName,
|
||||
jobName: string,
|
||||
buildNumber: string,
|
||||
): Promise<void>;
|
||||
}
|
||||
|
||||
export class JenkinsApiImpl implements JenkinsApi {
|
||||
@@ -135,7 +139,14 @@ export class JenkinsApiImpl implements JenkinsApi {
|
||||
},
|
||||
});
|
||||
|
||||
return (await response.json()).projects;
|
||||
return (
|
||||
(await response.json()).projects?.map((p: Project) => ({
|
||||
...p,
|
||||
onRestartClick: async () => {
|
||||
await this.retry(entity, p.fullName, String(p.lastBuild.number));
|
||||
},
|
||||
})) || []
|
||||
);
|
||||
}
|
||||
|
||||
async getBuild(
|
||||
@@ -160,14 +171,23 @@ export class JenkinsApiImpl implements JenkinsApi {
|
||||
return (await response.json()).build;
|
||||
}
|
||||
|
||||
retry(
|
||||
// @ts-ignore unused because unimplemented
|
||||
async retry(
|
||||
entity: EntityName,
|
||||
// @ts-ignore unused because unimplemented
|
||||
jobName: string,
|
||||
// @ts-ignore unused because unimplemented
|
||||
buildNumber: string,
|
||||
): Promise<any> {
|
||||
return Promise.resolve(undefined);
|
||||
): Promise<void> {
|
||||
const url = `${await this.discoveryApi.getBaseUrl('jenkins')}/v1/entity/${
|
||||
entity.namespace
|
||||
}/${entity.kind}/${entity.name}/job/${encodeURIComponent(
|
||||
jobName,
|
||||
)}/${buildNumber}:rebuild`;
|
||||
|
||||
const idToken = await this.identityApi.getIdToken();
|
||||
await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
...(idToken && { Authorization: `Bearer ${idToken}` }),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ const useStyles = makeStyles(theme => ({
|
||||
}));
|
||||
|
||||
const BuildWithStepsView = () => {
|
||||
// TODO: Add a test that react-router decodes this (even though `generatePath` doesn't encode it for you!)
|
||||
const { jobName, buildNumber } = useRouteRefParams(buildRouteRef);
|
||||
const classes = useStyles();
|
||||
|
||||
|
||||
@@ -100,11 +100,11 @@ const generatedColumns: TableColumn[] = [
|
||||
<Link
|
||||
component={RouterLink}
|
||||
to={generatePath(buildRouteRef.path, {
|
||||
jobName: row.fullName,
|
||||
buildNumber: row.lastBuild?.number.toString(),
|
||||
jobName: encodeURIComponent(row.fullName),
|
||||
buildNumber: String(row.lastBuild?.number),
|
||||
})}
|
||||
>
|
||||
{row.fullName}
|
||||
{row.fullDisplayName}
|
||||
</Link>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { useCallback } from 'react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useAsyncRetry } from 'react-use';
|
||||
import { jenkinsApiRef } from '../api';
|
||||
import { useAsyncPolling } from './useAsyncPolling';
|
||||
@@ -32,7 +32,7 @@ export function useBuildWithSteps(jobName: string, buildNumber: string) {
|
||||
const api = useApi(jenkinsApiRef);
|
||||
const errorApi = useApi(errorApiRef);
|
||||
const { entity } = useEntity();
|
||||
const entityName = getEntityName(entity);
|
||||
const entityName = useMemo(() => getEntityName(entity), [entity]);
|
||||
|
||||
const getBuildWithSteps = useCallback(async () => {
|
||||
try {
|
||||
@@ -43,14 +43,6 @@ export function useBuildWithSteps(jobName: string, buildNumber: string) {
|
||||
}
|
||||
}, [buildNumber, jobName, entityName, api, errorApi]);
|
||||
|
||||
const restartBuild = async () => {
|
||||
try {
|
||||
await api.retry(entityName, jobName, buildNumber);
|
||||
} catch (e) {
|
||||
errorApi.post(e);
|
||||
}
|
||||
};
|
||||
|
||||
const { loading, value, retry } = useAsyncRetry(() => getBuildWithSteps(), [
|
||||
getBuildWithSteps,
|
||||
]);
|
||||
@@ -63,7 +55,6 @@ export function useBuildWithSteps(jobName: string, buildNumber: string) {
|
||||
return [
|
||||
{ loading, value, retry },
|
||||
{
|
||||
restartBuild,
|
||||
getBuildWithSteps,
|
||||
startPolling,
|
||||
stopPolling,
|
||||
|
||||
Reference in New Issue
Block a user