@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/plugin-jenkins': patch
|
||||
'@backstage/plugin-jenkins-backend': patch
|
||||
'@backstage/plugin-jenkins-common': patch
|
||||
---
|
||||
|
||||
These packages have been migrated to the [backstage/community-plugins](https://github.com/backstage/community-plugins) repository.
|
||||
@@ -1,255 +1,3 @@
|
||||
# Jenkins Plugin (Alpha)
|
||||
# Deprecated
|
||||
|
||||
Welcome to the Jenkins backend plugin! Website: [https://jenkins.io/](https://jenkins.io/)
|
||||
|
||||
This is the backend half of the 2 Jenkins plugins and is responsible for:
|
||||
|
||||
- finding an appropriate instance of Jenkins for an entity
|
||||
- finding the appropriate job(s) on that instance for an entity
|
||||
- connecting to Jenkins and gathering data to present to the frontend
|
||||
|
||||
## New Backend System
|
||||
|
||||
The jenkins backend plugin has support for the [new backend system](https://backstage.io/docs/backend-system/), here's how you can set that up:
|
||||
|
||||
In your `packages/backend/src/index.ts` make the following changes:
|
||||
|
||||
```diff
|
||||
import { createBackend } from '@backstage/backend-defaults';
|
||||
const backend = createBackend();
|
||||
// ... other feature additions
|
||||
backend.add(import('@backstage/plugin-jenkins-backend'));
|
||||
backend.start();
|
||||
```
|
||||
|
||||
## Integrating into a backstage instance
|
||||
|
||||
This plugin needs to be added to an existing backstage instance.
|
||||
|
||||
```bash
|
||||
# From your Backstage root directory
|
||||
yarn --cwd packages/backend add @backstage/plugin-jenkins-backend
|
||||
```
|
||||
|
||||
Typically, this means creating a `src/plugins/jenkins.ts` file and adding a reference to it to `src/index.ts`
|
||||
|
||||
### jenkins.ts
|
||||
|
||||
```typescript
|
||||
import {
|
||||
createRouter,
|
||||
DefaultJenkinsInfoProvider,
|
||||
} from '@backstage/plugin-jenkins-backend';
|
||||
import { CatalogClient } from '@backstage/catalog-client';
|
||||
import { Router } from 'express';
|
||||
import { PluginEnvironment } from '../types';
|
||||
|
||||
export default async function createPlugin(
|
||||
env: PluginEnvironment,
|
||||
): Promise<Router> {
|
||||
const catalog = new CatalogClient({
|
||||
discoveryApi: env.discovery,
|
||||
});
|
||||
|
||||
return await createRouter({
|
||||
logger: env.logger,
|
||||
jenkinsInfoProvider: DefaultJenkinsInfoProvider.fromConfig({
|
||||
config: env.config,
|
||||
catalog,
|
||||
}),
|
||||
permissions: env.permissions,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### src/index.ts
|
||||
|
||||
```diff
|
||||
diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts
|
||||
index f2b14b2..2c64f47 100644
|
||||
--- a/packages/backend/src/index.ts
|
||||
+++ b/packages/backend/src/index.ts
|
||||
@@ -22,6 +22,7 @@ import { Config } from '@backstage/config';
|
||||
import app from './plugins/app';
|
||||
+import jenkins from './plugins/jenkins';
|
||||
import scaffolder from './plugins/scaffolder';
|
||||
@@ -56,6 +57,7 @@ async function main() {
|
||||
const authEnv = useHotMemoize(module, () => createEnv('auth'));
|
||||
+ const jenkinsEnv = useHotMemoize(module, () => createEnv('jenkins'));
|
||||
const proxyEnv = useHotMemoize(module, () => createEnv('proxy'));
|
||||
@@ -63,6 +65,7 @@ async function main() {
|
||||
|
||||
const apiRouter = Router();
|
||||
apiRouter.use('/catalog', await catalog(catalogEnv));
|
||||
+ apiRouter.use('/jenkins', await jenkins(jenkinsEnv));
|
||||
apiRouter.use('/scaffolder', await scaffolder(scaffolderEnv));
|
||||
```
|
||||
|
||||
This plugin must be provided with a JenkinsInfoProvider, this is a strategy object for finding the Jenkins instance and job for an entity.
|
||||
|
||||
There is a standard one provided, but the Integrator is free to build their own.
|
||||
|
||||
### DefaultJenkinsInfoProvider
|
||||
|
||||
Allows configuration of either a single or multiple global Jenkins instances and annotating entities with the job name on that instance (and optionally the name of the instance).
|
||||
|
||||
#### Example - Single global instance
|
||||
|
||||
The following will look for jobs for this entity at `https://jenkins.example.com/job/teamA/job/artistLookup-build`
|
||||
|
||||
Config
|
||||
|
||||
```yaml
|
||||
jenkins:
|
||||
baseUrl: https://jenkins.example.com
|
||||
username: backstage-bot
|
||||
apiKey: 123456789abcdef0123456789abcedf012
|
||||
# optionally add extra headers
|
||||
# extraRequestHeaders:
|
||||
# extra-header: my-value
|
||||
```
|
||||
|
||||
Catalog
|
||||
|
||||
```yaml
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: artist-lookup
|
||||
annotations:
|
||||
'jenkins.io/job-full-name': teamA/artistLookup-build
|
||||
```
|
||||
|
||||
The old annotation name of `jenkins.io/github-folder` is equivalent to `jenkins.io/job-full-name`
|
||||
|
||||
#### Example - Multiple global instances
|
||||
|
||||
The following will look for jobs for this entity at `https://jenkins-foo.example.com/job/teamA/job/artistLookup-build`
|
||||
|
||||
Config
|
||||
|
||||
```yaml
|
||||
jenkins:
|
||||
instances:
|
||||
- name: default
|
||||
baseUrl: https://jenkins.example.com
|
||||
username: backstage-bot
|
||||
apiKey: 123456789abcdef0123456789abcedf012
|
||||
- name: departmentFoo
|
||||
baseUrl: https://jenkins-foo.example.com
|
||||
username: backstage-bot
|
||||
apiKey: 123456789abcdef0123456789abcedf012
|
||||
```
|
||||
|
||||
Catalog
|
||||
|
||||
```yaml
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: artist-lookup
|
||||
annotations:
|
||||
'jenkins.io/job-full-name': departmentFoo:teamA/artistLookup-build
|
||||
```
|
||||
|
||||
If the `departmentFoo:` part is omitted, the default instance will be assumed.
|
||||
|
||||
The following config is an equivalent (but less clear) version of the above:
|
||||
|
||||
```yaml
|
||||
jenkins:
|
||||
baseUrl: https://jenkins.example.com
|
||||
username: backstage-bot
|
||||
apiKey: 123456789abcdef0123456789abcedf012
|
||||
instances:
|
||||
- name: departmentFoo
|
||||
baseUrl: https://jenkins-foo.example.com
|
||||
username: backstage-bot
|
||||
apiKey: 123456789abcdef0123456789abcedf012
|
||||
```
|
||||
|
||||
### Custom JenkinsInfoProvider
|
||||
|
||||
An example of a bespoke JenkinsInfoProvider which uses an organisation specific annotation to look up the Jenkins info (including jobFullName):
|
||||
|
||||
```typescript
|
||||
class AcmeJenkinsInfoProvider implements JenkinsInfoProvider {
|
||||
constructor(private readonly catalog: CatalogClient) {}
|
||||
|
||||
async getInstance(opt: {
|
||||
entityRef: EntityName;
|
||||
jobFullName?: string;
|
||||
}): Promise<JenkinsInfo> {
|
||||
const PAAS_ANNOTATION = 'acme.example.com/paas-project-name';
|
||||
|
||||
// lookup pass-project-name from entity annotation
|
||||
const entity = await this.catalog.getEntityByRef(opt.entityRef);
|
||||
if (!entity) {
|
||||
throw new Error(
|
||||
`Couldn't find entity with name: ${stringifyEntityRef(opt.entityRef)}`,
|
||||
);
|
||||
}
|
||||
|
||||
const paasProjectName = entity.metadata.annotations?.[PAAS_ANNOTATION];
|
||||
if (!paasProjectName) {
|
||||
throw new Error(
|
||||
`Couldn't find paas annotation (${PAAS_ANNOTATION}) on entity with name: ${stringifyEntityRef(
|
||||
opt.entityRef,
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
|
||||
// lookup department and team for paas project name
|
||||
const { team, dept } = this.lookupPaasInfo(paasProjectName);
|
||||
|
||||
const baseUrl = `https://jenkins-${dept}.example.com/`;
|
||||
const jobFullName = `${team}/${paasProjectName}`;
|
||||
const username = 'backstage-bot';
|
||||
const apiKey = this.getJenkinsApiKey(paasProjectName);
|
||||
const creds = btoa(`${username}:${apiKey}`);
|
||||
|
||||
return {
|
||||
baseUrl,
|
||||
headers: {
|
||||
Authorization: `Basic ${creds}`,
|
||||
},
|
||||
jobFullName,
|
||||
};
|
||||
}
|
||||
|
||||
private lookupPaasInfo(_: string): { team: string; dept: string } {
|
||||
// Mock implementation, this would get info from the paas system somehow in reality.
|
||||
return {
|
||||
team: 'teamA',
|
||||
dept: 'DepartmentFoo',
|
||||
};
|
||||
}
|
||||
|
||||
private getJenkinsApiKey(_: string): string {
|
||||
// Mock implementation, this would get info from the paas system somehow in reality.
|
||||
return '123456789abcdef0123456789abcedf012';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
No config would be needed if using this JenkinsInfoProvider
|
||||
|
||||
A Catalog entity of the following will look for jobs for this entity at `https://jenkins-departmentFoo.example.com/job/teamA/job/artistLookupService`
|
||||
|
||||
```yaml
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: artist-lookup
|
||||
annotations:
|
||||
'acme.example.com/paas-project-name': artistLookupService
|
||||
```
|
||||
|
||||
## Jenkins' terminology notes
|
||||
|
||||
The domain model for Jenkins is not particularly clear but for the purposes of this plugin the following model has been assumed:
|
||||
|
||||
Jenkins contains a tree of *job*s which have children of either; other *job*s (making it a _folder_) or *build*s (making it a _project_).
|
||||
Concepts like _pipeline_ and *view*s are meaningless (pipelines are just jobs for our purposes, views are (as the name suggests) just views of subsets of jobs)
|
||||
|
||||
A _job full name_ is a slash separated list of the names of the job, and the folders which contain it. For example `teamA/artistLookupService/develop`, and the same way that a filesystem path has folders and file names.
|
||||
This package has been moved to the [backstage-community/plugins](https://github.com/backstage/community-plugins) repository. Migrate to using `@backstage-community/plugin-jenkins-backend` instead.
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
"version": "0.4.4",
|
||||
"description": "A Backstage backend plugin that integrates towards Jenkins",
|
||||
"backstage": {
|
||||
"role": "backend-plugin"
|
||||
"role": "backend-plugin",
|
||||
"moved": "@backstage-community/plugin-jenkins-backend"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
@@ -56,5 +57,6 @@
|
||||
"@types/jenkins": "^1.0.0",
|
||||
"@types/supertest": "^2.0.8"
|
||||
},
|
||||
"configSchema": "config.d.ts"
|
||||
"configSchema": "config.d.ts",
|
||||
"deprecated": "This package has been moved to the backstage/community-plugins repository. You should migrate to using @backstage-community/plugin-jenkins-backend instead."
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# Jenkins Common
|
||||
# Deprecated
|
||||
|
||||
Shared isomorphic code for the Jenkins plugin.
|
||||
This package has been moved to the [backstage-community/plugins](https://github.com/backstage/community-plugins) repository. Migrate to using `@backstage-community/plugin-jenkins-common` instead.
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
"name": "@backstage/plugin-jenkins-common",
|
||||
"version": "0.1.25",
|
||||
"backstage": {
|
||||
"role": "common-library"
|
||||
"role": "common-library",
|
||||
"moved": "@backstage-community/plugin-jenkins-common"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
@@ -37,5 +38,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "workspace:^"
|
||||
}
|
||||
},
|
||||
"deprecated": "This package has been moved to the backstage/community-plugins repository. You should migrate to using @backstage-community/plugin-jenkins-common instead."
|
||||
}
|
||||
|
||||
+2
-134
@@ -1,135 +1,3 @@
|
||||
# Jenkins Plugin (Alpha)
|
||||
# Deprecated
|
||||
|
||||
Website: [https://jenkins.io/](https://jenkins.io/)
|
||||
|
||||
<img src="./src/assets/last-master-build.png" alt="Last master build"/>
|
||||
<img src="./src/assets/folder-results.png" alt="Folder results"/>
|
||||
<img src="./src/assets/build-details.png" alt="Build details"/>
|
||||
<img src="./src/assets/jobrun-table.png" alt="Job builds records"/>
|
||||
<img src="./src/assets/dynamic-columns.png" alt="Modify Table Columns"/>
|
||||
|
||||
## Setup
|
||||
|
||||
1. If you have a standalone app (you didn't clone this repo), then do
|
||||
|
||||
```bash
|
||||
# From your Backstage root directory
|
||||
yarn --cwd packages/app add @backstage/plugin-jenkins
|
||||
```
|
||||
|
||||
2. Add and configure the [jenkins-backend](../jenkins-backend) plugin according to it's instructions
|
||||
|
||||
3. Add the `EntityJenkinsContent` extension to the `CI/CD` page and `EntityLatestJenkinsRunCard` to the `overview` page in the app (or wherever you'd prefer):
|
||||
|
||||
Note that if you configured a custom JenkinsInfoProvider in step 2, you may need a custom isJenkinsAvailable. Also if you're transitioning to a new default branch name, you can pass multiple branch names as a comma-separated list and it will check for each branch name.
|
||||
|
||||
```tsx
|
||||
// In packages/app/src/components/catalog/EntityPage.tsx
|
||||
import {
|
||||
EntityJenkinsContent,
|
||||
EntityLatestJenkinsRunCard,
|
||||
isJenkinsAvailable,
|
||||
} from '@backstage/plugin-jenkins';
|
||||
|
||||
// You can add the tab to any number of pages, the service page is shown as an
|
||||
// example here
|
||||
const serviceEntityPage = (
|
||||
<EntityLayout>
|
||||
<EntityLayout.Route path="/" title="Overview">
|
||||
{/* ... */}
|
||||
<EntitySwitch>
|
||||
<EntitySwitch.Case if={isJenkinsAvailable}>
|
||||
<Grid item sm={6}>
|
||||
<EntityLatestJenkinsRunCard
|
||||
branch="main,master"
|
||||
variant="gridItem"
|
||||
/>
|
||||
</Grid>
|
||||
</EntitySwitch.Case>
|
||||
{/* ... */}
|
||||
</EntitySwitch>
|
||||
</EntityLayout.Route>
|
||||
{/* other tabs... */}
|
||||
<EntityLayout.Route path="/ci-cd" title="CI/CD">
|
||||
<EntitySwitch>
|
||||
<EntitySwitch.Case if={isJenkinsAvailable}>
|
||||
<EntityJenkinsContent />
|
||||
</EntitySwitch.Case>
|
||||
{/* ... */}
|
||||
</EntitySwitch>
|
||||
</EntityLayout.Route>
|
||||
{/* ... */}
|
||||
</EntityLayout>
|
||||
);
|
||||
```
|
||||
|
||||
4. Run app with `yarn start`
|
||||
5. Add the Jenkins folder annotation to your `catalog-info.yaml`.
|
||||
|
||||
Currently, this plugin only supports folders and Git SCM.
|
||||
Note that if you configured a custom JenkinsInfoProvider in step 2, you may need to use a different annotation scheme here
|
||||
|
||||
```yaml
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: 'your-component'
|
||||
description: 'a description'
|
||||
annotations:
|
||||
jenkins.io/github-folder: 'folder-name/project-name' # deprecated
|
||||
jenkins.io/job-full-name: 'folder-name/project-name' # use this instead
|
||||
|
||||
spec:
|
||||
type: service
|
||||
lifecycle: experimental
|
||||
owner: your-name
|
||||
```
|
||||
|
||||
7. Register your component
|
||||
|
||||
8. Click the component in the catalog. You should now see Jenkins builds, and a
|
||||
last build result for your master build.
|
||||
|
||||
## Features
|
||||
|
||||
- View all runs inside a folder
|
||||
- Last build status for specified branch
|
||||
- View summary of a build
|
||||
|
||||
## Limitations
|
||||
|
||||
- Only works with organization folder projects backed by GitHub
|
||||
- No pagination support currently, limited to 50 projects - don't run this on a
|
||||
Jenkins instance with lots of builds
|
||||
|
||||
## EntityJobRunsTable
|
||||
|
||||
- View all builds of a particular job
|
||||
- shows average build time for successful builds
|
||||
|
||||
## Modify Columns of EntityJenkinsContent
|
||||
|
||||
- now you can pass down column props to show the columns/metadata as per your use case.
|
||||
|
||||
```tsx
|
||||
export const generatedColumns: TableColumn[] = [
|
||||
{
|
||||
title: 'Timestamp',
|
||||
field: 'lastBuild.timestamp',
|
||||
render: (row: Partial<Project>) => (
|
||||
<>
|
||||
<Typography paragraph>
|
||||
{`
|
||||
${new Date(row.lastBuild?.timestamp).toLocaleDateString()}
|
||||
${new Date(row.lastBuild?.timestamp).toLocaleTimeString()}
|
||||
`}
|
||||
</Typography>
|
||||
</>
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
// ...
|
||||
<EntityJenkinsContent columns={generatedColumns}/>
|
||||
// ...
|
||||
```
|
||||
This package has been moved to the [backstage-community/plugins](https://github.com/backstage/community-plugins) repository. Migrate to using `@backstage-community/plugin-jenkins` instead.
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
"version": "0.9.9",
|
||||
"description": "A Backstage plugin that integrates towards Jenkins",
|
||||
"backstage": {
|
||||
"role": "frontend-plugin"
|
||||
"role": "frontend-plugin",
|
||||
"moved": "@backstage-community/plugin-jenkins"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
@@ -62,5 +63,6 @@
|
||||
"react": "^16.13.1 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^16.13.1 || ^17.0.0 || ^18.0.0",
|
||||
"react-router-dom": "6.0.0-beta.0 || ^6.3.0"
|
||||
}
|
||||
},
|
||||
"deprecated": "This package has been moved to the backstage/community-plugins repository. You should migrate to using @backstage-community/plugin-jenkins instead."
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user