fix: gitea/refactor file saving (#492)

This commit is contained in:
Denys Konovalov 2023-02-06 22:30:19 +01:00 committed by GitHub
parent f89d10627a
commit 1abc18b0ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 71 additions and 39 deletions

View File

@ -362,26 +362,29 @@ export default class API {
async persistFiles(dataFiles: DataFile[], mediaFiles: AssetProxy[], options: PersistOptions) { async persistFiles(dataFiles: DataFile[], mediaFiles: AssetProxy[], options: PersistOptions) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
[mediaFiles, dataFiles].forEach(async list => { const files: (DataFile | AssetProxy)[] = mediaFiles.concat(dataFiles as any);
list.forEach(async file => { for (const file of files) {
const item: { raw?: string; sha?: string; toBase64?: () => Promise<string> } = file; const item: { raw?: string; sha?: string; toBase64?: () => Promise<string> } = file;
const contentBase64 = await result( const contentBase64 = await result(
item, item,
'toBase64', 'toBase64',
partial(this.toBase64, item.raw as string), partial(this.toBase64, item.raw as string),
); );
if (options.newEntry) { try {
await this.request(`${this.repoURL}/contents/${file.path}`, {
method: 'POST',
body: JSON.stringify({
branch: this.branch,
content: contentBase64,
message: options.commitMessage,
signoff: false,
}),
});
} else {
const oldSha = await this.getFileSha(file.path); const oldSha = await this.getFileSha(file.path);
await this.updateBlob(contentBase64, file, options, oldSha!);
} catch {
await this.createBlob(contentBase64, file, options);
}
}
}
async updateBlob(
contentBase64: string,
file: AssetProxy | DataFile,
options: PersistOptions,
oldSha: string,
) {
await this.request(`${this.repoURL}/contents/${file.path}`, { await this.request(`${this.repoURL}/contents/${file.path}`, {
method: 'PUT', method: 'PUT',
body: JSON.stringify({ body: JSON.stringify({
@ -393,7 +396,16 @@ export default class API {
}), }),
}); });
} }
});
async createBlob(contentBase64: string, file: AssetProxy | DataFile, options: PersistOptions) {
await this.request(`${this.repoURL}/contents/${file.path}`, {
method: 'POST',
body: JSON.stringify({
branch: this.branch,
content: contentBase64,
message: options.commitMessage,
signoff: false,
}),
}); });
} }
@ -415,12 +427,12 @@ export default class API {
if (file) { if (file) {
return file.sha; return file.sha;
} else { } else {
console.error('File not found'); throw new APIError('Not Found', 404, API_NAME);
} }
} }
async deleteFiles(paths: string[], message: string) { async deleteFiles(paths: string[], message: string) {
paths.forEach(async file => { for (const file of paths) {
const meta: ContentsResponse = await this.request(`${this.repoURL}/contents/${file}`, { const meta: ContentsResponse = await this.request(`${this.repoURL}/contents/${file}`, {
method: 'GET', method: 'GET',
}); });
@ -428,7 +440,7 @@ export default class API {
method: 'DELETE', method: 'DELETE',
body: JSON.stringify({ branch: this.branch, message, sha: meta.sha, signoff: false }), body: JSON.stringify({ branch: this.branch, message, sha: meta.sha, signoff: false }),
}); });
}); }
} }
toBase64(str: string) { toBase64(str: string) {

View File

@ -101,7 +101,7 @@ describe('gitea API', () => {
}); });
describe('persistFiles', () => { describe('persistFiles', () => {
it('should create a new file', async () => { it('should check if file exists and create a new file', async () => {
const api = new API({ branch: 'master', repo: 'owner/repo' }); const api = new API({ branch: 'master', repo: 'owner/repo' });
const responses = { const responses = {
@ -127,9 +127,13 @@ describe('gitea API', () => {
newEntry: true, newEntry: true,
}); });
expect(api.request).toHaveBeenCalledTimes(1); expect(api.request).toHaveBeenCalledTimes(2);
expect((api.request as jest.Mock).mock.calls[0]).toEqual([ expect((api.request as jest.Mock).mock.calls[0]).toEqual([
'/repos/owner/repo/git/trees/master:content%2Fposts',
]);
expect((api.request as jest.Mock).mock.calls[1]).toEqual([
'/repos/owner/repo/contents/content/posts/new-post.md', '/repos/owner/repo/contents/content/posts/new-post.md',
{ {
method: 'POST', method: 'POST',
@ -174,11 +178,25 @@ describe('gitea API', () => {
newEntry: false, newEntry: false,
}); });
expect(api.request).toHaveBeenCalledTimes(1); expect(api.request).toHaveBeenCalledTimes(2);
expect((api.request as jest.Mock).mock.calls[0]).toEqual([ expect((api.request as jest.Mock).mock.calls[0]).toEqual([
'/repos/owner/repo/git/trees/master:content%2Fposts', '/repos/owner/repo/git/trees/master:content%2Fposts',
]); ]);
expect((api.request as jest.Mock).mock.calls[1]).toEqual([
'/repos/owner/repo/contents/content/posts/update-post.md',
{
method: 'PUT',
body: JSON.stringify({
branch: 'master',
content: Base64.encode(entry.dataFiles[0].raw),
message: 'commitMessage',
sha: 'old-sha',
signoff: false,
}),
},
]);
}); });
}); });

View File

@ -9,13 +9,15 @@ beta: true
For repositories stored on Gitea, the `gitea` backend allows CMS users to log in directly with their Gitea account. Note that all users must have push access to your content repository for this to work. For repositories stored on Gitea, the `gitea` backend allows CMS users to log in directly with their Gitea account. Note that all users must have push access to your content repository for this to work.
<Alert severity="warning">Because of the [lack](https://github.com/go-gitea/gitea/issues/14619) of a Gitea API endpoint for multifile commits, when using this backend, separate commits are created for every changed file. Please make sure this is handled correctly by your CI.</Alert>
## Authentication ## Authentication
Because Gitea requires a server for authentication and Netlify doesn't support Gitea, a custom OAuth provider needs to be used for basic Gitea authentication. Because Gitea requires a server for authentication and Netlify doesn't support Gitea, a custom OAuth provider needs to be used for basic Gitea authentication.
To enable basic Gitea authentication: To enable basic Gitea authentication:
1. Setup an own OAuth provider, for example with [scm-oauth](https://github.com/denyskon/scm-oauth-provider). 1. Setup an own OAuth provider, for example with [Teabag](https://github.com/denyskon/teabag).
2. Add the following lines to your Static CMS `config` file: 2. Add the following lines to your Static CMS `config` file:
<CodeTabs> <CodeTabs>