Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(releases): creating releases in git backend #6854

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
339 changes: 85 additions & 254 deletions dev-test/config.yml
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should probably not link to your repo

Large diffs are not rendered by default.

38,091 changes: 38,091 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions packages/netlify-cms-backend-github/src/API.ts
Expand Up @@ -818,6 +818,27 @@ export default class API {
return cmsBranches;
}

async listReleases() {
console.log(
'%c Loading releases',
'line-height: 30px;text-align: center;font-weight: bold',
);

return this.request(`${this.repoURL}/releases`)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you may want to do error handeling here ?


async publishRelease(version: string) {
console.log(
`%c Publishing release ${version}`,
'line-height: 30px;text-align: center;font-weight: bold',
);

return this.request(`${this.repoURL}/releases`, {
method: 'POST',
body: JSON.stringify({ tag_name: version }),
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

}

async listUnpublishedBranches() {
console.log(
'%c Checking for Unpublished entries',
Expand Down Expand Up @@ -1449,6 +1470,7 @@ export default class API {
author?: GitHubAuthor,
committer?: GitHubCommitter,
) {
console.log(message);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks like it should either be removed or switched to the logging format.

const result: Octokit.GitCreateCommitResponse = await this.request(
`${this.repoURL}/git/commits`,
{
Expand Down
16 changes: 16 additions & 0 deletions packages/netlify-cms-backend-github/src/implementation.tsx
Expand Up @@ -579,6 +579,22 @@ export default class GitHub implements Implementation {
};
}

async listReleases() {
try {
return await this.api!.listReleases();
} catch (e) {
return [];
}
}

async publishRelease(version: string) {
try {
return await this.api!.publishRelease(version);
} catch (e) {
return;
}
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the rest of the code prefers the .then() syntax, you should probably respect that and write it as:

return await this.api!.listReleases().catch(e => ([]))

async unpublishedEntries() {
const listEntriesKeys = () =>
this.api!.listUnpublishedBranches().then(branches =>
Expand Down
26 changes: 26 additions & 0 deletions packages/netlify-cms-backend-gitlab/src/API.ts
Expand Up @@ -1015,4 +1015,30 @@ export default class API {
const mergeRequest = await this.getBranchMergeRequest(branch);
return mergeRequest.sha;
}

async listReleases() {
console.log(
'%c Loading releases',
'line-height: 30px;text-align: center;font-weight: bold',
);

return this.request(`${this.repoURL}/releases`)
}

async publishRelease(version: string) {
console.log(
`%c Publishing release ${version}`,
'line-height: 30px;text-align: center;font-weight: bold',
);

return this.requestJSON({
url: `${this.repoURL}/releases`,
method: 'POST',
headers: { 'Content-Type': 'application/json; charset=utf-8' },
body: JSON.stringify({
tag_name: version,
ref: this.branch,
}),
});
}
}
18 changes: 18 additions & 0 deletions packages/netlify-cms-backend-gitlab/src/implementation.ts
Expand Up @@ -453,4 +453,22 @@ export default class GitLab implements Implementation {
return null;
}
}

async listReleases() {
try {
const response = await this.api!.listReleases();
const json = await response.json()
return json;
} catch (e) {
return [];
}
}

async publishRelease(version: string) {
try {
return await this.api!.publishRelease(version);
} catch (e) {
return;
}
}
}
5 changes: 3 additions & 2 deletions packages/netlify-cms-core/package.json
Expand Up @@ -43,10 +43,10 @@
"jwt-decode": "^3.0.0",
"node-polyglot": "^2.3.0",
"prop-types": "^15.7.2",
"react": "^16.8.4",
"react": "^16.8.4 || ^17.0.0",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should probably justify this react deps bump

"react-dnd": "^14.0.0",
"react-dnd-html5-backend": "^14.0.0",
"react-dom": "^16.8.4",
"react-dom": "^16.8.4 || ^17.0.0",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

"react-frame-component": "^5.2.1",
"react-hot-loader": "^4.8.0",
"react-immutable-proptypes": "^2.1.0",
Expand All @@ -71,6 +71,7 @@
"remark-gfm": "1.0.0",
"sanitize-filename": "^1.6.1",
"semaphore": "^1.0.5",
"semver": "^7.5.4",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please document why you want semver

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to conveniently bump the version. will document

"tomlify-j0.4": "^3.0.0-alpha.0",
"url": "^0.11.0",
"url-join": "^4.0.1",
Expand Down
137 changes: 137 additions & 0 deletions packages/netlify-cms-core/src/actions/releases.ts
@@ -0,0 +1,137 @@
import { actions as notifActions } from 'redux-notifications';
import { currentBackend } from '../backend';
import type {
State,
Releases,
} from '../types/redux';
import type { AnyAction } from 'redux';
import type { ThunkDispatch } from 'redux-thunk';

const { notifSend } = notifActions;

/*
* Constant Declarations
*/
export const RELEASES_REQUEST = 'RELEASES_REQUEST';
export const RELEASES_SUCCESS = 'RELEASES_SUCCESS';
export const RELEASES_FAILURE = 'RELEASES_FAILURE';
export const RELEASE_PUBLICATION_REQUEST = 'RELEASE_PUBLICATION_REQUEST';
export const RELEASE_PUBLICATION_SUCCESS = 'RELEASE_PUBLICATION_SUCCESS';
export const RELEASE_PUBLICATION_FAILURE = 'RELEASE_PUBLICATION_FAILURE';

/*
* Simple Action Creators (Internal)
*/

function releasesLoading() {
return {
type: RELEASES_REQUEST,
};
}

function releasesLoaded(releases: Releases) {
return {
type: RELEASES_SUCCESS,
payload: releases,
};
}

function releasesFailed(error: Error) {
return {
type: RELEASES_FAILURE,
error: 'Failed to load releases',
payload: error,
};
}

function releasePublicationLoading() {
return {
type: RELEASE_PUBLICATION_REQUEST,
};
}

function releasePublicationSuccess() {
console.log(RELEASE_PUBLICATION_SUCCESS);
return {
type: RELEASE_PUBLICATION_SUCCESS,
};
}

function releasePublicationFailed(error: Error) {
return {
type: RELEASE_PUBLICATION_FAILURE,
error: 'Failed to publish release',
payload: error,
};
}

/*
* Exported Thunk Action Creators
*/

export function loadReleases() {
return (dispatch: ThunkDispatch<State, {}, AnyAction>, getState: () => State) => {
const state = getState();
const backend = currentBackend(state.config);

dispatch(releasesLoading());
backend
.listReleases()
.then(response => dispatch(releasesLoaded(response)))
.catch((error: Error) => {
dispatch(
notifSend({
message: {
key: 'ui.toast.onFailToLoadEntries',
details: error,
},
kind: 'danger',
dismissAfter: 8000,
}),
);
dispatch(releasesFailed(error));
Promise.reject(error);
});
};
}

export function createRelease(version: 'string') {
return async (dispatch: ThunkDispatch<State, {}, AnyAction>, getState: () => State) => {
const state = getState();
const backend = currentBackend(state.config);

dispatch(releasePublicationLoading());

try {
await backend.publishRelease(version)
dispatch(
notifSend({
message: {
key: 'ui.toast.releasePublished',
},
kind: 'success',
dismissAfter: 4000,
}),
);
dispatch(releasePublicationSuccess());
} catch (error: any) {
dispatch(
notifSend({
message: {
key: 'ui.toast.onReleaseFailed',
details: error,
},
kind: 'danger',
dismissAfter: 8000,
}),
);
return Promise.reject(
dispatch(releasePublicationFailed(error)),
);
} finally {
setTimeout(() => {
dispatch(loadReleases())
}, 2000);
}
};
}
8 changes: 8 additions & 0 deletions packages/netlify-cms-core/src/backend.ts
Expand Up @@ -924,6 +924,14 @@ export class Backend {
}
}

async listReleases() {
return await this.implementation.listReleases!();
}

async publishRelease(version: string) {
await this.implementation.publishRelease(version);
}

async unpublishedEntries(collections: Collections) {
const ids = await this.implementation.unpublishedEntries!();
const entries = (
Expand Down
4 changes: 4 additions & 0 deletions packages/netlify-cms-core/src/components/App/App.js
Expand Up @@ -22,6 +22,7 @@ import Workflow from '../Workflow/Workflow';
import Editor from '../Editor/Editor';
import NotFoundPage from './NotFoundPage';
import Header from './Header';
import Releases from '../Releases/Releases';

TopBarProgress.config({
barColors: {
Expand Down Expand Up @@ -172,6 +173,7 @@ class App extends React.Component {

const defaultPath = getDefaultPath(collections);
const hasWorkflow = publishMode === EDITORIAL_WORKFLOW;
const isGitBackend = currentBackend(this.props.config).isGitBackend();

return (
<>
Expand All @@ -186,6 +188,7 @@ class App extends React.Component {
displayUrl={config.display_url}
isTestRepo={config.backend.name === 'test-repo'}
showMediaButton={showMediaButton}
isGitBackend={isGitBackend}
/>
<AppMainContainer>
{isFetching && <TopBarProgress />}
Expand All @@ -205,6 +208,7 @@ class App extends React.Component {
to={defaultPath}
/>
{hasWorkflow ? <Route path="/workflow" component={Workflow} /> : null}
{isGitBackend ? <Route path="/releases" component={Releases}/> : null }
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems to only work for github, is checking isGitBackend() enough to ensure we're in GitHub land ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was planning to add implementations for the other providers.

<RouteInCollection
exact
collections={collections}
Expand Down
10 changes: 10 additions & 0 deletions packages/netlify-cms-core/src/components/App/Header.js
Expand Up @@ -126,6 +126,7 @@ class Header extends React.Component {
isTestRepo: PropTypes.bool,
t: PropTypes.func.isRequired,
checkBackendStatus: PropTypes.func.isRequired,
isGitBackend: PropTypes.bool.isRequired,
};

intervalId;
Expand Down Expand Up @@ -158,6 +159,7 @@ class Header extends React.Component {
isTestRepo,
t,
showMediaButton,
isGitBackend,
} = this.props;

const createableCollections = collections
Expand Down Expand Up @@ -195,6 +197,14 @@ class Header extends React.Component {
</AppHeaderButton>
</li>
)}
{isGitBackend && (
<li>
<AppHeaderNavLink to="/releases" activeClassName="header-link-active">
<Icon type="rocket" />
{t('app.header.releases')}
</AppHeaderNavLink>
</li>
)}
</AppHeaderNavList>
</nav>
<AppHeaderActions>
Expand Down