Skip to content

Commit 1660448

Browse files
Merge branch 'main' into release/use-scoped-variables
2 parents bd7d3d2 + b5dbc29 commit 1660448

File tree

14 files changed

+288
-122
lines changed

14 files changed

+288
-122
lines changed
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
name: Validate Pull Request
2+
3+
on:
4+
pull_request:
5+
types:
6+
- opened
7+
- synchronize
8+
- edited
9+
- reopened
10+
branches:
11+
- 'main'
12+
- 'release-**'
13+
# paths-ignore:
14+
# - 'docs/**'
15+
# - '.github/'
16+
# - 'CHANGELOG/'
17+
# - 'charts/'
18+
# - 'manifests/'
19+
# - 'sample-docker-templates/'
20+
21+
jobs:
22+
validate-PR-issue:
23+
runs-on: ubuntu-latest
24+
permissions:
25+
issues: write
26+
contents: read
27+
pull-requests: write
28+
steps:
29+
- name: Checkout repository
30+
uses: actions/checkout@v2
31+
32+
- name: Validate Issue Reference
33+
env:
34+
GITHUB_TOKEN: ${{ github.token }}
35+
PR_BODY: ${{ github.event.pull_request.body }}
36+
url: ${{ github.event.pull_request.url }}
37+
PRNUM: ${{ github.event.pull_request.number }}
38+
TITLE: ${{ github.event.pull_request.title }}
39+
run: |
40+
set -x
41+
if [[ "$TITLE" == *"doc:"* || "$TITLE" == *"docs:"* || "$TITLE" == *"chore:"* ]]; then
42+
echo "Skipping validation as this is a PR for documentation or chore."
43+
gh pr edit $PRNUM --remove-label "PR:Issue-verification-failed"
44+
gh pr edit $PRNUM --add-label "PR:Ready-to-Review"
45+
exit 0
46+
fi
47+
48+
### For ex: Fixes #2123
49+
pattern1="((Fixes|Resolves) #[0-9]+)"
50+
51+
### For ex: Resolves https://github.com/devtron-labs/devtron/issues/2123
52+
pattern2="((Fixes|Resolves) https://github.com/devtron-labs/devtron/issues/[0-9]+)"
53+
54+
### For ex: Fixes devtron-labs/devtron#2123
55+
pattern3="((Fixes|Resolves) devtron-labs/devtron#[0-9]+)"
56+
57+
# Get the pull request body
58+
PR_BODY=$(jq -r '.pull_request.body' $GITHUB_EVENT_PATH)
59+
echo "PR_BODY = $PR_BODY"
60+
61+
### Checks if PR_BODY matches pattern1 or pattern2 or pattern3 or none
62+
### grep -i (case insensitive) -E (enables extended regular expression in grep) -q (this option suppresses normal output)
63+
if echo "$PR_BODY" | grep -iEq "$pattern1"; then
64+
### Here we are taking only the numerical value ie. issue number
65+
### head -n1 only prints the 1st line.
66+
### grep -o -E "[0-9]+ basically outputs only the number between [0-9]+
67+
issue_num=$(echo "$PR_BODY" | grep -iE "$pattern1" | head -n1 | grep -o -E "[0-9]+")
68+
echo "issue_num is : $issue_num"
69+
elif echo "$PR_BODY" | grep -iEq "$pattern2"; then
70+
issue_num=$(echo "$PR_BODY" | grep -iE "$pattern2" | head -n1 | awk -F '/' '{print $NF}')
71+
echo "issue_num is : $issue_num"
72+
elif echo "$PR_BODY" | grep -iEq "$pattern3"; then
73+
issue_num=$(echo "$PR_BODY" | grep -iE "$pattern3" | head -n1 | awk -F '#' '{print $NF}')
74+
else
75+
echo "No Issue number detected hence failing the PR Validation check."
76+
gh pr edit $PRNUM --add-label "PR:Issue-verification-failed"
77+
gh pr edit $PRNUM --remove-label "PR:Ready-to-Review"
78+
exit 1
79+
fi
80+
81+
### hardcoding the url here to devtron repo as issues will always be created inside devtron repo.
82+
url=https://api.github.com/repos/devtron-labs/devtron
83+
84+
# Add the issue number to the URL
85+
url="${url}/issues/${issue_num}"
86+
echo "$url"
87+
response_code=$(curl -s -o /dev/null -w "%{http_code}" "$url")
88+
if [[ "$response_code" -eq 200 ]]; then
89+
# Check if issue is open or closed
90+
text=$(curl -s "$url")
91+
echo "checking status of the issue"
92+
# Skipping this condition as the Issue can be in closed state as BE PRs are merged before FE
93+
# if [[ $(echo "$text" | jq -r '.state') == "open" ]]; then
94+
echo "Issue #$issue_num is open"
95+
echo "Issue reference found in the pull request body."
96+
gh pr edit $PRNUM --remove-label "PR:Issue-verification-failed"
97+
gh pr edit $PRNUM --add-label "PR:Ready-to-Review"
98+
exit 0
99+
# else
100+
# echo "Issue #$issue_num is not open"
101+
# exit 1
102+
# fi
103+
else
104+
echo "Invalid Response Code obtained - error code: $response_code"
105+
echo "No valid issue reference found in the pull request body."
106+
gh pr comment $PRNUM --body "PR is not linked to any issue, please make the corresponding changes in the body."
107+
gh pr edit $PRNUM --add-label "PR:Issue-verification-failed"
108+
gh pr edit $PRNUM --remove-label "PR:Ready-to-Review"
109+
exit 1
110+
fi

src/components/app/details/appDetails/utils.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -698,7 +698,7 @@ export const processDeploymentStatusDetailsData = (
698698
deploymentData.deploymentStatusText = 'Failed'
699699
deploymentData.deploymentStatusBreakdown.APP_HEALTH.displaySubText = 'Failed'
700700
} else if (data?.wfrStatus === 'Progressing') {
701-
deploymentData.deploymentStatus = DEPLOYMENT_STATUS.PROGRESSING
701+
deploymentData.deploymentStatus = DEPLOYMENT_STATUS.INPROGRESS
702702
deploymentData.deploymentStatusText = 'In progress'
703703
}
704704
}

src/components/external-apps/ExternalAppService.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export interface ChartMetadata {
8181
notes?: string
8282
}
8383

84-
interface HelmReleaseStatus {
84+
export interface HelmReleaseStatus {
8585
status: string,
8686
message: string,
8787
description: string

src/components/userGroups/Group.tsx

Lines changed: 85 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
1-
import React, { useState, useEffect, useContext, useRef } from 'react';
2-
import { deepEqual } from '../common';
1+
import React, { useState, useEffect, useContext, useRef } from 'react'
2+
import { deepEqual } from '../common'
33
import { showError, Progressing, DeleteDialog, ResizableTextarea } from '@devtron-labs/devtron-fe-common-lib'
4-
import { saveGroup, deleteGroup } from './userGroup.service';
4+
import { saveGroup, deleteGroup } from './userGroup.service'
55

66
import {
77
DirectPermissionsRoleFilter,
88
ChartGroupPermissionsFilter,
99
EntityTypes,
1010
ActionTypes,
1111
CreateGroup,
12-
} from './userGroups.types';
13-
import './UserGroup.scss';
14-
import { toast } from 'react-toastify';
15-
import AppPermissions from './AppPermissions';
16-
import { ACCESS_TYPE_MAP, SERVER_MODE } from '../../config';
17-
import { mainContext } from '../common/navigation/NavigationRoutes';
12+
} from './userGroups.types'
13+
import './UserGroup.scss'
14+
import { toast } from 'react-toastify'
15+
import AppPermissions from './AppPermissions'
16+
import { ACCESS_TYPE_MAP, SERVER_MODE } from '../../config'
17+
import { mainContext } from '../common/navigation/NavigationRoutes'
1818
import { ReactComponent as Warning } from '../../assets/icons/ic-warning.svg'
19-
import { excludeKeyAndClusterValue } from './K8sObjectPermissions/K8sPermissions.utils';
19+
import { excludeKeyAndClusterValue } from './K8sObjectPermissions/K8sPermissions.utils'
2020

2121
export default function GroupForm({
2222
id = null,
@@ -28,72 +28,72 @@ export default function GroupForm({
2828
cancelCallback,
2929
}) {
3030
// id null is for create
31-
const { serverMode } = useContext(mainContext);
32-
const [directPermission, setDirectPermission] = useState<DirectPermissionsRoleFilter[]>([]);
31+
const { serverMode } = useContext(mainContext)
32+
const [directPermission, setDirectPermission] = useState<DirectPermissionsRoleFilter[]>([])
3333
const [chartPermission, setChartPermission] = useState<ChartGroupPermissionsFilter>({
3434
entity: EntityTypes.CHART_GROUP,
3535
action: ActionTypes.VIEW,
3636
entityName: [],
37-
});
38-
const [submitting, setSubmitting] = useState(false);
39-
const [k8sPermission, setK8sPermission] = useState<any[]>([]);
40-
const [name, setName] = useState({ value: '', error: '' });
41-
const [description, setDescription] = useState('');
42-
const [deleteConfirmationModal, setDeleteConfirmationModal] = useState(false);
37+
})
38+
const [submitting, setSubmitting] = useState(false)
39+
const [k8sPermission, setK8sPermission] = useState<any[]>([])
40+
const [name, setName] = useState({ value: '', error: '' })
41+
const [description, setDescription] = useState('')
42+
const [deleteConfirmationModal, setDeleteConfirmationModal] = useState(false)
4343
const currentK8sPermissionRef = useRef<any[]>([])
4444

4545
function isFormComplete(): boolean {
46-
let isComplete: boolean = true;
46+
let isComplete: boolean = true
4747
const tempPermissions = directPermission.reduce((agg, curr) => {
4848
if (curr.team && curr.entityName.length === 0) {
49-
isComplete = false;
50-
curr.entityNameError = 'Applications are mandatory';
49+
isComplete = false
50+
curr.entityNameError = 'Applications are mandatory'
5151
}
5252
if (curr.team && curr.environment.length === 0) {
53-
isComplete = false;
54-
curr.environmentError = 'Environments are mandatory';
53+
isComplete = false
54+
curr.environmentError = 'Environments are mandatory'
5555
}
56-
agg.push(curr);
57-
return agg;
58-
}, []);
56+
agg.push(curr)
57+
return agg
58+
}, [])
5959

6060
if (!isComplete) {
61-
setDirectPermission(tempPermissions);
61+
setDirectPermission(tempPermissions)
6262
}
6363

64-
return isComplete;
64+
return isComplete
6565
}
6666

6767
function getSelectedEnvironments(permission) {
68-
if (permission.accessType === ACCESS_TYPE_MAP.DEVTRON_APPS) {
69-
return permission.environment.find((env) => env.value === '*')
70-
? ''
71-
: permission.environment.map((env) => env.value).join(',');
72-
} else {
73-
let allFutureCluster = {};
74-
let envList = '';
75-
permission.environment.forEach((element) => {
76-
if (element.clusterName === '' && element.value.startsWith('#')) {
77-
const clusterName = element.value.substring(1);
78-
allFutureCluster[clusterName] = true;
79-
envList += (envList !== '' ? ',' : '') + clusterName + '__*';
80-
} else if (element.clusterName !== '' && !allFutureCluster[element.clusterName]) {
81-
envList += (envList !== '' ? ',' : '') + element.value;
82-
}
83-
});
84-
return envList;
85-
}
86-
}
68+
if (permission.accessType === ACCESS_TYPE_MAP.DEVTRON_APPS) {
69+
return permission.environment.find((env) => env.value === '*')
70+
? ''
71+
: permission.environment.map((env) => env.value).join(',')
72+
} else {
73+
let allFutureCluster = {}
74+
let envList = ''
75+
permission.environment.forEach((element) => {
76+
if (element.clusterName === '' && element.value.startsWith('#')) {
77+
const clusterName = element.value.substring(1)
78+
allFutureCluster[clusterName] = true
79+
envList += (envList !== '' ? ',' : '') + clusterName + '__*'
80+
} else if (element.clusterName !== '' && !allFutureCluster[element.clusterName]) {
81+
envList += (envList !== '' ? ',' : '') + element.value
82+
}
83+
})
84+
return envList
85+
}
86+
}
8787

8888
async function handleSubmit(e) {
8989
if (!name.value) {
90-
setName((name) => ({ ...name, error: 'Group name is mandatory' }));
91-
return;
90+
setName((name) => ({ ...name, error: 'Group name is mandatory' }))
91+
return
9292
}
9393
if (!isFormComplete()) {
94-
return;
94+
return
9595
}
96-
setSubmitting(true);
96+
setSubmitting(true)
9797
const payload: CreateGroup = {
9898
id: id || 0,
9999
name: name.value,
@@ -106,72 +106,74 @@ export default function GroupForm({
106106
)
107107
.map((permission) => ({
108108
...permission,
109-
action: permission.action.value,
109+
action: permission.action.configApprover
110+
? `${permission.action.value},configApprover`
111+
: permission.action.value,
110112
team: permission.team.value,
111113
environment: getSelectedEnvironments(permission),
112114
entityName: permission.entityName.find((entity) => entity.value === '*')
113115
? ''
114116
: permission.entityName.map((entity) => entity.value).join(','),
115117
})),
116-
...k8sPermission.map((permission) => ({
117-
...permission,
118-
entity: EntityTypes.CLUSTER,
119-
action: permission.action.value,
120-
cluster: permission.cluster.label,
121-
group: permission.group.value === '*' ? '' : permission.group.value,
122-
kind: permission.kind.value === '*' ? '' : permission.kind.label,
123-
namespace: permission.namespace.value === '*' ? '' : permission.namespace.value,
124-
resource: permission.resource.find((entity) => entity.value === '*')
118+
...k8sPermission.map((permission) => ({
119+
...permission,
120+
entity: EntityTypes.CLUSTER,
121+
action: permission.action.value,
122+
cluster: permission.cluster.label,
123+
group: permission.group.value === '*' ? '' : permission.group.value,
124+
kind: permission.kind.value === '*' ? '' : permission.kind.label,
125+
namespace: permission.namespace.value === '*' ? '' : permission.namespace.value,
126+
resource: permission.resource.find((entity) => entity.value === '*')
125127
? ''
126-
: permission.resource.map((entity) => entity.value).join(',')
127-
}))
128+
: permission.resource.map((entity) => entity.value).join(','),
129+
})),
128130
],
129-
};
131+
}
130132
if (serverMode !== SERVER_MODE.EA_ONLY) {
131133
payload.roleFilters.push({
132134
...chartPermission,
133135
team: '',
134136
environment: '',
135137
entityName: chartPermission.entityName.map((entity) => entity.value).join(','),
136-
});
138+
})
137139
}
138140

139141
try {
140-
const { result } = await saveGroup(payload);
142+
const { result } = await saveGroup(payload)
141143
if (id) {
142144
currentK8sPermissionRef.current = [...k8sPermission].map(excludeKeyAndClusterValue)
143-
updateCallback(index, result);
144-
toast.success('Group updated');
145+
updateCallback(index, result)
146+
toast.success('Group updated')
145147
} else {
146-
createCallback(result);
147-
toast.success('Group createed');
148+
createCallback(result)
149+
toast.success('Group createed')
148150
}
149151
} catch (err) {
150-
showError(err);
152+
showError(err)
151153
} finally {
152-
setSubmitting(false);
154+
setSubmitting(false)
153155
}
154156
}
155157
useEffect(() => {
156-
groupData && populateDataFromAPI(groupData);
157-
}, [groupData]);
158+
groupData && populateDataFromAPI(groupData)
159+
}, [groupData])
158160

159161
async function populateDataFromAPI(data: CreateGroup) {
160-
const { id, name, description } = data;
161-
setName({ value: name, error: '' });
162-
setDescription(description);
162+
const { id, name, description } = data
163+
setName({ value: name, error: '' })
164+
setDescription(description)
163165
}
164166

165167
async function handleDelete() {
166-
setSubmitting(true);
168+
setSubmitting(true)
167169
try {
168-
await deleteGroup(id);
169-
toast.success('Group deleted');
170-
deleteCallback(index);
170+
await deleteGroup(id)
171+
toast.success('Group deleted')
172+
deleteCallback(index)
171173
} catch (err) {
172-
showError(err);
174+
showError(err)
173175
} finally {
174-
setSubmitting(false);
176+
setSubmitting(false)
175177
}
176178
}
177179
return (

0 commit comments

Comments
 (0)