|
|
@@ -7,7 +7,11 @@ import { ChartType, RepoType, StorageType } from '../../../../shared/types';
|
|
|
import { Context } from '../../../../shared/Context';
|
|
|
|
|
|
import ImageSelector from '../../../../components/image-selector/ImageSelector';
|
|
|
+import RepoSelector from '../../../../components/repo-selector/RepoSelector';
|
|
|
import SaveButton from '../../../../components/SaveButton';
|
|
|
+import Heading from '../../../../components/values-form/Heading';
|
|
|
+import Helper from '../../../../components/values-form/Helper';
|
|
|
+import InputRow from '../../../../components/values-form/InputRow';
|
|
|
|
|
|
type PropsType = {
|
|
|
currentChart: ChartType,
|
|
|
@@ -16,18 +20,30 @@ type PropsType = {
|
|
|
};
|
|
|
|
|
|
type StateType = {
|
|
|
+ sourceType: string,
|
|
|
selectedImageUrl: string | null,
|
|
|
selectedTag: string | null,
|
|
|
saveValuesStatus: string | null,
|
|
|
values: string,
|
|
|
+ selectedRepo: RepoType | null,
|
|
|
+ selectedBranch: string,
|
|
|
+ subdirectory: string,
|
|
|
+ webhookToken: string,
|
|
|
+ highlightCopyButton: boolean,
|
|
|
};
|
|
|
|
|
|
export default class SettingsSection extends Component<PropsType, StateType> {
|
|
|
state = {
|
|
|
+ sourceType: 'registry',
|
|
|
selectedImageUrl: '',
|
|
|
selectedTag: '',
|
|
|
values: '',
|
|
|
saveValuesStatus: null as (string | null),
|
|
|
+ selectedRepo: null as RepoType | null,
|
|
|
+ selectedBranch: '',
|
|
|
+ subdirectory: '',
|
|
|
+ webhookToken: '',
|
|
|
+ highlightCopyButton: false,
|
|
|
}
|
|
|
|
|
|
// TODO: read in set image from form context instead of config
|
|
|
@@ -50,7 +66,7 @@ export default class SettingsSection extends Component<PropsType, StateType> {
|
|
|
if (err) {
|
|
|
console.log(err)
|
|
|
} else {
|
|
|
- console.log(res.data.webhook_token)
|
|
|
+ this.setState({ webhookToken: res.data.webhook_token })
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
@@ -58,11 +74,19 @@ export default class SettingsSection extends Component<PropsType, StateType> {
|
|
|
redeployWithNewImage = (img: string, tag: string) => {
|
|
|
this.setState({ saveValuesStatus: 'loading' });
|
|
|
let { currentCluster, currentProject } = this.context;
|
|
|
+
|
|
|
+ // If tag is explicitly declared, parse tag
|
|
|
+ let imgSplits = img.split(':');
|
|
|
+ let parsedTag = null;
|
|
|
+ if (imgSplits.length > 1) {
|
|
|
+ img = imgSplits[0];
|
|
|
+ parsedTag = imgSplits[1];
|
|
|
+ }
|
|
|
+
|
|
|
let image = {
|
|
|
image: {
|
|
|
- // TODO: prepend registry
|
|
|
repository: img,
|
|
|
- tag: tag,
|
|
|
+ tag: parsedTag || tag,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -86,11 +110,16 @@ export default class SettingsSection extends Component<PropsType, StateType> {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- render() {
|
|
|
- return (
|
|
|
- <Wrapper>
|
|
|
- <StyledSettingsSection>
|
|
|
- <Subtitle>Connected source</Subtitle>
|
|
|
+ renderSourceSection = () => {
|
|
|
+ if (this.state.sourceType === 'registry') {
|
|
|
+ return (
|
|
|
+ <>
|
|
|
+ <Helper>
|
|
|
+ Specify a container image and tag or
|
|
|
+ <Highlight onClick={() => this.setState({ sourceType: 'repo' })}>
|
|
|
+ link a repo
|
|
|
+ </Highlight>.
|
|
|
+ </Helper>
|
|
|
<ImageSelector
|
|
|
selectedImageUrl={this.state.selectedImageUrl}
|
|
|
selectedTag={this.state.selectedTag}
|
|
|
@@ -99,6 +128,67 @@ export default class SettingsSection extends Component<PropsType, StateType> {
|
|
|
forceExpanded={true}
|
|
|
setCurrentView={this.props.setCurrentView}
|
|
|
/>
|
|
|
+ </>
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ let { currentProject } = this.context;
|
|
|
+ return (
|
|
|
+ <>
|
|
|
+ <Helper>
|
|
|
+ Select a repo to connect to. You can
|
|
|
+ <A padRight={true} href={`/api/oauth/projects/${currentProject.id}/github?redirected=true`}>
|
|
|
+ log in with GitHub
|
|
|
+ </A> or
|
|
|
+ <Highlight onClick={() => this.setState({ sourceType: 'registry' })}>
|
|
|
+ link an image registry
|
|
|
+ </Highlight>.
|
|
|
+ </Helper>
|
|
|
+ <RepoSelector
|
|
|
+ forceExpanded={true}
|
|
|
+ selectedRepo={this.state.selectedRepo}
|
|
|
+ selectedBranch={this.state.selectedBranch}
|
|
|
+ subdirectory={this.state.subdirectory}
|
|
|
+ setSelectedRepo={(x: RepoType) => this.setState({ selectedRepo: x })}
|
|
|
+ setSelectedBranch={(x: string) => this.setState({ selectedBranch: x })}
|
|
|
+ setSubdirectory={(x: string) => this.setState({ subdirectory: x })}
|
|
|
+ />
|
|
|
+ </>
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ renderWebhookSection = () => {
|
|
|
+ if (this.state.webhookToken) {
|
|
|
+ let webhookText = `curl -X POST 'https://dashboard.getporter.dev/api/webhooks/deploy/${this.state.webhookToken}?commit=???&repository=???'`;
|
|
|
+ return (
|
|
|
+ <>
|
|
|
+ <Heading>Redeploy Webhook</Heading>
|
|
|
+ <Helper>Programmatically deploy by calling this secret webhook.</Helper>
|
|
|
+ <Webhook copiedToClipboard={this.state.highlightCopyButton}>
|
|
|
+ <div>{webhookText}</div>
|
|
|
+ <i
|
|
|
+ className="material-icons"
|
|
|
+ onClick={() => {
|
|
|
+ navigator.clipboard.writeText(webhookText);
|
|
|
+ this.setState({ highlightCopyButton: true });
|
|
|
+ }}
|
|
|
+ onMouseLeave={() => this.setState({ highlightCopyButton: false })}
|
|
|
+ >
|
|
|
+ content_copy
|
|
|
+ </i>
|
|
|
+ </Webhook>
|
|
|
+ </>
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ render() {
|
|
|
+ return (
|
|
|
+ <Wrapper>
|
|
|
+ <StyledSettingsSection>
|
|
|
+ <Heading>Connected source</Heading>
|
|
|
+ {this.renderSourceSection()}
|
|
|
+ {this.renderWebhookSection()}
|
|
|
</StyledSettingsSection>
|
|
|
<SaveButton
|
|
|
text='Save Settings'
|
|
|
@@ -114,19 +204,54 @@ export default class SettingsSection extends Component<PropsType, StateType> {
|
|
|
|
|
|
SettingsSection.contextType = Context;
|
|
|
|
|
|
-const Subtitle = styled.div`
|
|
|
- color: #aaaabb;
|
|
|
+const Webhook = styled.div`
|
|
|
+ width: 100%;
|
|
|
+ border: 1px solid #ffffff55;
|
|
|
+ background: #ffffff11;
|
|
|
+ border-radius: 3px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
font-size: 13px;
|
|
|
- margin-bottom: 15px;
|
|
|
- margin-top: 20px;
|
|
|
+ padding-left: 10px;
|
|
|
+ color: #aaaabb;
|
|
|
+ height: 40px;
|
|
|
+ position: relative;
|
|
|
+ margin-bottom: 40px;
|
|
|
+
|
|
|
+ > div {
|
|
|
+ user-select: all;
|
|
|
+ }
|
|
|
+
|
|
|
+ > i {
|
|
|
+ padding: 5px;
|
|
|
+ background: ${(props: { copiedToClipboard: boolean }) => props.copiedToClipboard ? '#616FEEcc' : '#ffffff22'};
|
|
|
+ border-radius: 5px;
|
|
|
+ position: absolute;
|
|
|
+ right: 10px;
|
|
|
+ font-size: 14px;
|
|
|
+ cursor: pointer;
|
|
|
+ color: #ffffff;
|
|
|
+
|
|
|
+ :hover {
|
|
|
+ background: ${(props: { copiedToClipboard: boolean }) => props.copiedToClipboard ? '' : '#ffffff44'};;
|
|
|
+ }
|
|
|
+ }
|
|
|
+`;
|
|
|
+
|
|
|
+const Highlight = styled.div`
|
|
|
+ color: #949eff;
|
|
|
+ text-decoration: underline;
|
|
|
+ margin-left: 5px;
|
|
|
+ cursor: pointer;
|
|
|
+ padding-right: ${(props: { padRight?: boolean }) => props.padRight ? '5px' : ''};
|
|
|
`;
|
|
|
|
|
|
-const Heading = styled.div`
|
|
|
- color: white;
|
|
|
- font-weight: 500;
|
|
|
- font-size: 16px;
|
|
|
- margin-top: 35px;
|
|
|
- margin-bottom: 22px;
|
|
|
+const A = styled.a`
|
|
|
+ color: #949eff;
|
|
|
+ text-decoration: underline;
|
|
|
+ margin-left: 5px;
|
|
|
+ cursor: pointer;
|
|
|
+ padding-right: ${(props: { padRight?: boolean }) => props.padRight ? '5px' : ''};
|
|
|
`;
|
|
|
|
|
|
const Wrapper = styled.div`
|
|
|
@@ -138,7 +263,7 @@ const StyledSettingsSection = styled.div`
|
|
|
width: 100%;
|
|
|
height: calc(100% - 60px);
|
|
|
background: #ffffff11;
|
|
|
- padding: 15px 35px 50px;
|
|
|
+ padding: 0 35px;
|
|
|
position: relative;
|
|
|
border-radius: 5px;
|
|
|
overflow: auto;
|