Create hello world app using Forge
Table of Contents
- 1. Create the app
- 2. jira-issue-panel template
- 3. Change the panel title
- 4. Install the app
- 5. Check your app
- 6. Login to Forge non interactively
- 7. Further changes to the app.
- 8. Calling Jira API
- 9. Make API calls on behalf of a user
- 10. Forge logs
- 11. Display current project's name
- 12. Forge tunnel debug
- 13. Custom icon in manifest.yml
- 14. Publish Forge apps
- 15. Check node and npm version
- 16. Forge errors
- 16.1. Error: error command sh -c node-gyp rebuild
- 16.2. Error: npm ERR! `nodegyp` is not a valid npm option
- 16.3. Error: sh: line 1: react-scripts: command not found
- 16.4. Error: error:0308010C:digital envelope routines::unsupported
- 16.5. Error: npm ERR! Missing script: "build"
- 16.6. Error: Keytar error detected
1 Create the app
- Navigate to the directory where you want to create the app
forge create
- Enter the name of the app:
hello-world-app
- Select a category:
UI kit
- Select a template:
jira-issue-panel
A new folder will be created.
cd hello-world-app
2 jira-issue-panel template
ls hello-world-app
3 Change the panel title
Go to the manifest.yml
and modify the title.
4 Install the app
Run this command from the top level directory.
forge deploy
Then install the app.
forge install
Select a product: Jira
Enter the site URL: abc-dev.atlassian.net
Then continue.
5 Check your app
Go to Manage apps section of your Jira instance and you will find your app installed on the instance.
Now create an issue in a project, create a project if not already done. You will find a button on top called Forge app for Ravi
or whatever title you used in the manifest.yml
file above.
Cool isn't it? How long it took you to create, deploy and install the app?
6 Login to Forge non interactively
forge login --email $FORGE_EMAIL --token $FORGE_TOKEN --non-interactive
6.1 Error: We couldn't log you in with those credentials
We couldn't log you in with those credentials. Check your email address and token before rerunning the command.
7 Further changes to the app.
You can do that using forge deploy
. So let us say you change the title, so after you run this command the changes will be reflected back.
This is a manual way of deploying the changes.
First make sure docker is running.
sudo systemctl start docker
Then run forge tunnel
.
At this stage you may encounter errors related to forge/cli, just follow the instructions on the screen.
- For me it didn't work properly. If I run docker with sudo then it works but not with normal user. Not a big problem because I can always use
forge deploy
. - Also run
sudo forge tunnel
if getCannot pull the tunnel image.
error.
I tried to pull the image directly using docker pull atlassian/forge-tunnel:latest
8 Calling Jira API
Now instead of display a static text, let us consume an end point.
8.1 Install @forge/api
npm install @forge/api
8.2 Make changes in your code
Check this page: https://developer.atlassian.com/platform/forge/call-a-jira-api/
8.3 Run forge tunnel
if not running already
When you add comments and refresh your page then you will see a message in your console showing number of comments.
8.4 Forge might ask to update permission.
If Forge complains about the permissions just follows its instructions.
8.5 Show last comment
Modify the code from Atlassian to display last comment along with number of comments.
const fetchLastCommentForIssue = async (issueId) => { const res = await api .asApp() .requestJira(`/rest/api/3/issue/${issueId}/comment`); const data = await res.json(); if (data.comments.length==0) { return "No comments yet, be the first one!" } else { const numberOfComments = data.comments.length const lastComment = data.comments[numberOfComments-1].body.content[0].content[0].text console.log(lastComment); return lastComment; } };
9 Make API calls on behalf of a user
In your index.jsx file change .asApp()
to .asUser()
. So your function will look like this.
const fetchLastCommentForIssue = async (issueId) => { const res = await api .asUser() .requestJira(`/rest/api/3/issue/${issueId}/comment`); const data = await res.json(); if (data.comments.length==0) { return "No comments yet, be the first one!" } else { const numberOfComments = data.comments.length const lastComment = data.comments[numberOfComments-1].body.content[0].content[0].text console.log(lastComment); return lastComment; } };
Now just run sudo forge tunnel
again and refresh the issue. You will get a button to allow access.
10 Forge logs
If you run forge logs
then you can check the recent log items. Can be helpful for debugging.
INFO 2021-03-28T14:09:01.909Z e4d5f610-047b-4e01-9942-813e53732c20 Number of comments on this issue: 4 INFO 2021-03-28T14:10:18.192Z f2b0e068-aa1a-40e0-b3f3-8f999a443158 Number of comments on this issue: 4 INFO 2021-03-28T14:10:50.324Z 8b63305e-cf41-4d13-ad6b-a915da77c15f Number of comments on this issue: 4 INFO 2021-03-28T14:11:27.599Z 605b4de8-70e4-473a-b160-a465dbdcf49b Number of comments on this issue: 4
11 Display current project's name
Let us display current issue's project name as well in the panel.
const fetchProjectFromIssue = async (issueId) => { const res = await api .asUser() .requestJira(`/rest/api/3/issue/${issueId}`); const data = await res.json(); return data.fields.project.name; }; //call this method to get the value const projectName = useState(async ()=> await fetchProjectFromIssue(context.platformContext.issueKey));
This is how your code will look like now.
import ForgeUI, { render, Fragment, Text, IssuePanel, useProductContext, useState } from '@forge/ui'; const App = () => { const context = useProductContext(); const [comments] = useState(async () => await fetchCommentsForIssue(context.platformContext.issueKey)); const lastComment = useState(async ()=> await fetchLastCommentForIssue(context.platformContext.issueKey)); const projectName = useState(async ()=> await fetchProjectFromIssue(context.platformContext.issueKey)); const message = `Number of comments on this issue: ${comments.length}`; console.log(message); console.log(lastComment); console.log(projectName); return ( <Fragment> <Text>Hello world! Great it works.</Text> <Text>{message}</Text> <Text>{lastComment[0]}</Text> <Text>{projectName[0]}</Text> </Fragment> ); }; export const run = render( <IssuePanel> <App /> </IssuePanel> ); const fetchCommentsForIssue = async (issueId) => { const res = await api .asUser() .requestJira(`/rest/api/3/issue/${issueId}/comment`); const data = await res.json(); return data.comments; }; const fetchLastCommentForIssue = async (issueId) => { const res = await api .asUser() .requestJira(`/rest/api/3/issue/${issueId}/comment`); const data = await res.json(); if (data.comments.length==0) { return "No comments yet, be the first one!" } else { const numberOfComments = data.comments.length const lastComment = data.comments[numberOfComments-1].body.content[0].content[0].text console.log(lastComment); return lastComment; } }; const fetchProjectFromIssue = async (issueId) => { const res = await api .asUser() .requestJira(`/rest/api/3/issue/${issueId}`); const data = await res.json(); return data.fields.project.name; };
12 Forge tunnel debug
Run forge tunnel debug
and open the url to check debugging. Add debugger;
in your code as well before that.
13 Custom icon in manifest.yml
I tried local relative path but it didn't work but then I tried an online path like this.
icon: https://www.sparxsys.com/sites/default/files/sparxsys_favicon_0.png
and it worked after uninstalling the app, then doing forge deploy
and forge install
.
14 Publish Forge apps
14.1 First deploy the app to production and then install it.
Details here: https://developer.atlassian.com/platform/forge/staging-and-production-apps/
forge deploy -e production forge install -e production
14.2 Send your app for approval
Once you have deployed and installed the app you can go to your vendor account and submit the app for approval. For there was already an app version, so I just had to go to the existing app and create a new version of the app for cloud.
14.3 Set pricing when your send your app for approval
I also set the app pricing during the approval
15 Check node and npm version
node --version npm --version
16 Forge errors
16.1 Error: error command sh -c node-gyp rebuild
https://github.com/nodejs/node-gyp/issues/2272
sudo npm install --global node-gyp@latest
npm config set node_gyp $(npm prefix -g)/lib/node_modules/node-gyp/bin/node-gyp.js
16.2 Error: npm ERR! `nodegyp` is not a valid npm option
16.3 Error: sh: line 1: react-scripts: command not found
Try adding '.env' file in root directory of your project and add this to file -
SKIPPREFLIGHTCHECK=true
I created this file in the react project not my forge app root.
16.4 Error: error:0308010C:digital envelope routines::unsupported
npm audit fix --force
16.5 Error: npm ERR! Missing script: "build"
16.6 Error: Keytar error detected
Error: Keytar error detected: Error spawning command line “dbus-launch –autolaunch=1dabfe8cd581451db7ec4cd4b3f8461b –binary-syntax –close-stderr”: Child process exited with code 1 Something went wrong while accessing Secret Service API.
I tried this forge settings set usage-analytics true
but it didn't work.