diff --git a/docs/appendix/part-2-code.md b/docs/appendix/part-2-code.md index 7cfcb6c..78e3375 100644 --- a/docs/appendix/part-2-code.md +++ b/docs/appendix/part-2-code.md @@ -1,9 +1,11 @@ # Part 2 - Action Code -Below is the code for the +Below is the code for the action that we will be creating in this exercise. This +action will download a random cat image from the Cat API and save it to the +workspace. ```typescript -// Imports +import { createTemplateAction } from "@backstage/plugin-scaffolder-node"; import fs from "fs"; import { Readable } from "stream"; import { ReadableStream } from "stream/web"; @@ -18,21 +20,50 @@ export interface CatResult { height: number; } -// handler function -ctx.logger.info( - `Running example template with parameters: ${ctx.input.myParameter}` -); +/** + * Creates an `acme:example` Scaffolder action. + * + * @remarks + * + * See {@link https://example.com} for more information. + * + * @public + */ +export function createAcmeExampleAction() { + // For more information on how to define custom actions, see + // https://backstage.io/docs/features/software-templates/writing-custom-actions + return createTemplateAction<{ + myParameter: string; + }>({ + id: "catscanner:randomcat", + description: "Downloads a random cat image into the workspace", + schema: { + input: { + type: "object", + required: [], + properties: {}, + }, + }, + async handler(ctx) { + ctx.logger.info( + `Running example template with parameters: ${ctx.input.myParameter}` + ); -const catResult = await fetch("https://api.thecatapi.com/v1/images/search"); + const catResult = await fetch( + "https://api.thecatapi.com/v1/images/search" + ); -const catData: Record = await catResult.json(); + const catData: Record = await catResult.json(); -const stream = fs.createWriteStream( - path.join(ctx.workspacePath, "catimage.jpeg") -); -const { body } = await fetch(catData[0].url); + const stream = fs.createWriteStream( + path.join(ctx.workspacePath, "catimage.jpeg") + ); + const { body } = await fetch(catData[0].url); -await finished(Readable.fromWeb(body as ReadableStream).pipe(stream)); + await finished(Readable.fromWeb(body as ReadableStream).pipe(stream)); -ctx.logger.info("Cat image downloaded"); + ctx.logger.info("Cat image downloaded"); + }, + }); +} ``` diff --git a/docs/getting-started.md b/docs/getting-started.md index 2eca7ed..e8e7d65 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -83,6 +83,15 @@ Once you have a running installation we can move to the first exercise! and 7007. The codespace is mapping the ports to your local ports. Ensure you have no other processes running on those ports. +??? Note Node 20 + + If you are running node 20 you will need to set the `NODE_OPTIONS=--no-node-snapshot` + environment variable to prevent the node process from crashing. + + You can do this in a few ways, such as running + `NODE_OPTIONS=--no-node-snapshot yarn dev` each time you start the app, or + by adding it to the `dev` script in the `package.json`. + ## Authentication For this workshop we will need to authenticate with GitHub in order to create diff --git a/docs/part-1.md b/docs/part-1.md index 03f31d9..a86a4c6 100644 --- a/docs/part-1.md +++ b/docs/part-1.md @@ -95,11 +95,7 @@ which will automatically register the actions and make them available to us. We need to add the following in `packages/backend/src/index.ts` ```typescript -import gitHubActions from "@backstage/plugin-scaffolder-backend-module-github"; - -... - -backend.add(gitHubActions()); +backend.add(import("@backstage/plugin-scaffolder-backend-module-github")); ``` Now, we can then use the actions we just installed in our template and create a @@ -116,19 +112,19 @@ a new repo using the `publish:github` action. Add the following to your !!! tip "Refreshing Template Changes" - The changes may not be immediately visible. You can the refresh the template by - going to the catalog, changing the "kind" filter to "Template", clicking on your - template and then clicking the refresh button in the about card. + The changes may not be immediately visible. You can the refresh the template + by going to the catalog, changing the "kind" filter to "Template", clicking + on your template and then clicking the refresh button in the about card. Now, go to your template, enter the repo details and it should create a new repo in GitHub for you. ??? Warning "Authentication" - Authentication is needed to enable your backstage to log into GitHub and create your repo. - If you are having issues go back to the Authentication section in Getting Started and ensure - you have a valid token. You will not be able to create new repos in philips-internal as by - default it tries to create public repos. + Authentication is needed to enable your backstage to log into GitHub and + create your repo. If you are having issues go back to the Authentication + section in Getting Started and ensure you have a valid token. You will only + be able to make repos in an org that you have access to create public repos. The above package installed a lot more actions than just the `publish:github`, [you can browse the rest of the available templates here](http://localhost:3000/create/actions). diff --git a/docs/part-2.md b/docs/part-2.md index 9d8be96..f5f0557 100644 --- a/docs/part-2.md +++ b/docs/part-2.md @@ -34,9 +34,9 @@ Congratulations, you have created a new scaffolder plugin! You can see what other plugin types there are by running `yarn new`. -## Register your plugin +## Install your plugin -The plugin won't do much on its own, we need to register it in the backend so +The plugin won't do much on its own, we need to install it in the backend so that it gets loaded correctly. Firstly, add your package as a reference to the @@ -112,22 +112,34 @@ and you should see `acme:example` in the list. (we will change this later!) ## Write your action Now we need to write our action. This is the code that will be executed when the -action is run as part of a template. +action is run as part of a template. We are going to use `example.ts` in our +plugin as a starting point. First, update the definition of the action, changing the ID and description and removing the required inputs and properties definitions. We don't need them -(yet). Your definition should something look like this: +(yet). Your call to `createTemplateAction` should something look like this: ```typescript -id: 'catscanner:randomcat', -description: 'Downloads a random cat image into the workspace', -schema: { - input: { - type: 'object', - required: [], - properties: {}, +return createTemplateAction<{ + myParameter: string; +}>({ + id: "catscanner:randomcat", + description: "Downloads a random cat image into the workspace", + schema: { + input: { + type: "object", + required: [], + properties: {}, + }, }, -}, + async handler(ctx) { + ctx.logger.info( + `Running example template with parameters: ${ctx.input.myParameter}` + ); + + await new Promise((resolve) => setTimeout(resolve, 1000)); + }, +}); ``` Now need to implement the action code in the `handler` function in @@ -143,7 +155,7 @@ log using `ctx.logger.info`. !!! tip "Unit Testing" - You should write unit tests for your action using the + You can and should write unit tests for your action using the [instructions in the backstage docs](https://backstage.io/docs/features/software-templates/writing-tests-for-actions). ## Use it in a template diff --git a/docs/part-3.md b/docs/part-3.md index 21fa52f..d364f56 100644 --- a/docs/part-3.md +++ b/docs/part-3.md @@ -68,6 +68,7 @@ We will create a new folder in `plugins/catscanner-react/src/components` to hold all of the files related to the extension. lets call this `RandomCatPix`. ```bash +cd backstage/ mkdir -p plugins/catscanner-react/src/components/RandomCatPix ``` @@ -80,9 +81,20 @@ component and the validation. These files are: - extensions.ts - contains the registration of the extension - index.ts - exports the extension to be used in the frontend -Create the file `RandomCatPixExtension.tsx` with the following contents. This is -the skeleton of our actual UI component and we will come back and extend this -with our implementation later. +We can create all these files by running the following commands: + +```bash +# go to the root of the react plugin +cd backstage/plugins/catscanner-react +touch src/components/RandomCatPix/RandomCatPixExtension.tsx \ + src/components/RandomCatPix/validation.ts \ + src/components/RandomCatPix/schema.ts \ + src/components/RandomCatPix/extensions.ts \ + src/components/RandomCatPix/index.ts +``` + +`RandomCatPixExtension.tsx` - This is the skeleton of our react UI component and +we will come back and extend this with our implementation later. ??? example "RandomCatPixExtension.tsx" @@ -113,9 +125,9 @@ with our implementation later. }; ``` -Create the file `validation.ts` with the following contents. This is the -validation of the output of the component. Inside this function, you can check -the value of the field and return an error if it is not valid. +`validation.ts` - This is the validation of the output of the component. Inside +this function, you can check the value of the field and return an error if it is +not valid. ??? example "validation.ts" @@ -133,9 +145,9 @@ the value of the field and return an error if it is not valid. ) => {}; ``` -Create the file `schema.ts` with the following contents. The schema is optional, -it allows backstage to know how to render this component, what inputs it takes. -This enables the previewing of the component in the Custom Field Explorer. +`schema.ts` - The schema is optional, it allows backstage to know how to render +this component, what inputs it takes. This enables the previewing of the +component in the Custom Field Explorer. ??? example "schema.ts" @@ -152,13 +164,11 @@ This enables the previewing of the component in the Custom Field Explorer. }; ``` -Now we need to create an `extensions.ts` file that will create the Field -Extension registration. This brings together the ID, React component and +`extensions.ts` - this will create the Field Extension registration that will be +recognized by backstage. This brings together the ID, React component and validation into a single component that we will make available to the scaffolder. -Create the `extensions.ts` file with the following contents. - ??? example "extensions.ts" ```typescript @@ -179,8 +189,8 @@ Create the `extensions.ts` file with the following contents. ); ``` -Now we need to export the component from the plugin by modifying the following -files +Now we need to export the component from the plugin by modifying the `index.ts` +in the `RandomCatPix` folder and in the components folder. ```typescript // plugins/catscanner-react/src/components/RandomCatPix/index.ts @@ -206,9 +216,10 @@ UI code that we will use to get a random cat image and display it for the user. You will want to look at the `useEffect` and `useState` react hooks to allow you to trigger the request to the API and store the result. -You must also call the `onChange` function passed as a property to pass any -output value along to the template. So you should call this each time change the -rendered cat image. +You must also call the `onChange` function passed as a props to notify backstage +of the output value you want to use in the template. This does not have to be +what you render directly in the UI, for example you can pass an ID but render a +friendly name. So you should call this each time change the rendered cat image. !!! tip @@ -219,6 +230,9 @@ rendered cat image. You do this in `packages/app/src/App.tsx`. You need to provide the `customFieldExtensions` as children to the `ScaffolderPage`. +In `App.tsx` you should find a section that looks like this, where the +scaffolder route is setup. + ```tsx const routes = ( @@ -229,7 +243,9 @@ const routes = ( ); ``` -Should be changed to look something like this +Currently it is empty as we do not have any custom UI components, only the built +in ones. We need to change the setup to take in our new custom field extension +so that the scaffolder is aware of it. ```tsx import { ScaffolderFieldExtensions } from "@backstage/plugin-scaffolder-react";