Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions .talismanrc
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
fileignoreconfig:

- filename: pnpm-lock.yaml
checksum: 98236e2588d5cffcbdda3464179d91e9540fd8ddb4d4fb5251cf0f7379876f42
- filename: packages/contentstack-bulk-operations/src/utils/backup-dir-asset-fetcher.ts
checksum: fbf1ef882bf4ea8f06a7a28b9248a07e01f5a5d2d4da98dbb9ee52bc16ce318a
- filename: packages/contentstack-bulk-operations/test/unit/utils/backup-dir-asset-fetcher.test.ts
checksum: b905524c952aff0f089fa5347e0db18479b75a438cd9ae77e2dfe1cb203d7e59
- filename: packages/contentstack-import/src/import/modules/assets.ts
checksum: 014f612c3e8db21c891da0c17a1a3b4cd340c026c58034791d86cd5f73aa9f90
- filename: pnpm-lock.yaml
checksum: 65d0adc160e75d17f5c8de732d554d352fefdc30751509b9ec4c8b2d250e3e2e
- filename: packages/contentstack-bulk-operations/test/unit/utils/revert-retry-handler.test.ts
checksum: 142e7b67a06bc13b6b4191b17f2ea8ae31a3bce20086ea35a92f3e3671eb603d
- filename: packages/contentstack-bulk-operations/src/commands/cm/stacks/bulk-assets.ts
checksum: 9392511b337c361b86c36eb2431d8fea9129a61f6b7d70dcb4a633fea339ca61
- filename: packages/contentstack-bulk-operations/src/utils/batch-helper.ts
checksum: ddcd8ef7c56d1122df88075883a2768a28813147e8fdef5b8e72a1ec118c045a
version: ""
- filename: packages/contentstack-import/src/import/modules/assets.ts
checksum: 014f612c3e8db21c891da0c17a1a3b4cd340c026c58034791d86cd5f73aa9f90
- filename: packages/contentstack-cli-tsgen/src/lib/helper.ts
checksum: cc2f88294ca026c29ca44ee7f9994ebc64e56d9a7a015c64070aaf271f1c3ba2
- filename: packages/contentstack-bulk-operations/src/commands/cm/stacks/bulk-assets.ts
checksum: 9392511b337c361b86c36eb2431d8fea9129a61f6b7d70dcb4a633fea339ca61
version: ""
2 changes: 2 additions & 0 deletions packages/contentstack-bootstrap/messages/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
"CLI_BOOTSTRAP_GITHUB_ACCESS_NOT_FOUND": "No Github access token found",
"CLI_BOOTSTRAP_START_CLONE_APP": "Cloning the selected app",
"CLI_BOOTSTRAP_REPO_NOT_FOUND": "Unable to find a repo for \"%s\"",
"CLI_BOOTSTRAP_APP_UNAVAILABLE": "Unable to download \"%s\": the repository or branch \"cli-use\" was not found. Ensure both exist on GitHub.",
"CLI_BOOTSTRAP_GITHUB_SERVER_ERROR": "Failed to download \"%s\": GitHub returned HTTP %s. Please try again later.",
Comment thread
cs-raj marked this conversation as resolved.
"CLI_BOOTSTRAP_NO_API_KEY_FOUND": "No API key generated for the stack",
"CLI_BOOTSTRAP_STACK_CREATION_FAILED": "Unable to create stack for content \"%s\"",
"CLI_BOOTSTRAP_APP_SELECTION_ENQUIRY": "Select an App",
Expand Down
1 change: 1 addition & 0 deletions packages/contentstack-bootstrap/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"mocha": "10.8.2",
"nyc": "^15.1.0",
"oclif": "^4.23.27",
"sinon": "^21.1.2",
"tmp": "^0.2.7",
"ts-node": "^8.10.2",
"typescript": "^5.9.3"
Expand Down
13 changes: 12 additions & 1 deletion packages/contentstack-bootstrap/src/bootstrap/github/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,24 @@ export default class GitHubClient {
}

const response = await HttpClient.create().options(options).get(url);

if (response.status < 200 || response.status >= 300) {
const message = response.status === 404
? messageHandler.parse('CLI_BOOTSTRAP_REPO_NOT_FOUND', `${this.repo.user}/${this.repo.name}`)
: messageHandler.parse('CLI_BOOTSTRAP_GITHUB_SERVER_ERROR', `${this.repo.user}/${this.repo.name}`, response.status);
throw new GithubError(message, response.status);
}
Comment thread
cs-raj marked this conversation as resolved.

return response.data as Stream;
}

async extract(destination: string, stream: Stream): Promise<any> {
return new Promise((resolve, reject) => {
const unzip = zlib.createUnzip();
stream.on('error', reject);
unzip.on('error', reject);
stream
.pipe(zlib.createUnzip())
.pipe(unzip)
.pipe(
tar.extract({
cwd: destination,
Expand Down
10 changes: 4 additions & 6 deletions packages/contentstack-bootstrap/src/bootstrap/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,13 @@ export default class Bootstrap {

try {
await this.ghClient.getLatest(this.cloneDirectory);
cliux.loader();
} catch (error) {
if (error instanceof GithubError) {
if (error.status === 404) {
cliux.error(messageHandler.parse('CLI_BOOTSTRAP_REPO_NOT_FOUND', this.appConfig.source));
}
cliux.loader();
if (error instanceof GithubError && error.status === 404) {
throw new Error(messageHandler.parse('CLI_BOOTSTRAP_APP_UNAVAILABLE', this.appConfig.source));
}
Comment thread
cs-raj marked this conversation as resolved.
Comment thread
cs-raj marked this conversation as resolved.
throw error;
} finally {
cliux.loader();
}

// seed plugin start
Expand Down
124 changes: 124 additions & 0 deletions packages/contentstack-bootstrap/test/github.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
const { expect } = require('chai');
const sinon = require('sinon');
const { Readable } = require('stream');
const { HttpClient } = require('@contentstack/cli-utilities');
Comment thread
cs-raj marked this conversation as resolved.
const GitHubClient = require('../lib/bootstrap/github/client').default;
const GithubError = require('../lib/bootstrap/github/github-error').default;

describe('Github Client', function () {
it('Parse github url', () => {
Expand All @@ -16,4 +20,124 @@ describe('Github Client', function () {
'https://api.github.com/repos/contentstack/contentstack-nextjs-react-universal-demo/tarball/cli-use',
);
});

describe('streamRelease', function () {
let sandbox;

beforeEach(() => {
sandbox = sinon.createSandbox();
});

afterEach(() => {
sandbox.restore();
});

it('should throw GithubError with status 404 when the branch does not exist', async () => {
const notFoundStream = new Readable({ read() {} });
notFoundStream.push(Buffer.from('404: Not Found'));
notFoundStream.push(null);

const httpStub = { get: sandbox.stub().resolves({ status: 404, data: notFoundStream }), options: sandbox.stub().returnsThis() };
sandbox.stub(HttpClient, 'create').returns(httpStub);

const client = new GitHubClient(GitHubClient.parsePath('contentstack/kickstart-next'));

try {
await client.streamRelease(client.gitTarBallUrl);
throw new Error('Expected GithubError to be thrown');
} catch (err) {
expect(err).to.be.instanceOf(GithubError);
expect(err.status).to.equal(404);
}
});

it('should throw GithubError with status 500 on server error', async () => {
const errStream = new Readable({ read() {} });
errStream.push(Buffer.from('Internal Server Error'));
errStream.push(null);

const httpStub = { get: sandbox.stub().resolves({ status: 500, data: errStream }), options: sandbox.stub().returnsThis() };
sandbox.stub(HttpClient, 'create').returns(httpStub);

const client = new GitHubClient(GitHubClient.parsePath('contentstack/kickstart-next'));

try {
await client.streamRelease(client.gitTarBallUrl);
throw new Error('Expected GithubError to be thrown');
} catch (err) {
expect(err).to.be.instanceOf(GithubError);
expect(err.status).to.equal(500);
}
});

it('should throw GithubError with status 302 on unexpected redirect', async () => {
const redirectStream = new Readable({ read() {} });
redirectStream.push(null);

const httpStub = { get: sandbox.stub().resolves({ status: 302, data: redirectStream }), options: sandbox.stub().returnsThis() };
sandbox.stub(HttpClient, 'create').returns(httpStub);

const client = new GitHubClient(GitHubClient.parsePath('contentstack/kickstart-next'));

try {
await client.streamRelease(client.gitTarBallUrl);
throw new Error('Expected GithubError to be thrown');
} catch (err) {
expect(err).to.be.instanceOf(GithubError);
expect(err.status).to.equal(302);
}
});

it('should return the response stream when status is 200', async () => {
const mockStream = new Readable({ read() {} });
const httpStub = { get: sandbox.stub().resolves({ status: 200, data: mockStream }), options: sandbox.stub().returnsThis() };
sandbox.stub(HttpClient, 'create').returns(httpStub);

const client = new GitHubClient(GitHubClient.parsePath('contentstack/kickstart-next'));
const result = await client.streamRelease(client.gitTarBallUrl);

expect(result).to.equal(mockStream);
});
Comment thread
cs-raj marked this conversation as resolved.

it('should pass Authorization header for private repos', async () => {
const mockStream = new Readable({ read() {} });
const httpStub = { get: sandbox.stub().resolves({ status: 200, data: mockStream }), options: sandbox.stub().returnsThis() };
sandbox.stub(HttpClient, 'create').returns(httpStub);

const client = new GitHubClient(GitHubClient.parsePath('contentstack/private-repo'), true, 'my-token');
await client.streamRelease(client.gitTarBallUrl);

const callOptions = httpStub.options.firstCall.args[0];
expect(callOptions.headers).to.deep.equal({ Authorization: 'token my-token' });
});

it('should throw GithubError immediately for private repos with no access token', async () => {
const client = new GitHubClient(GitHubClient.parsePath('contentstack/private-repo'), true, undefined);

try {
await client.streamRelease(client.gitTarBallUrl);
throw new Error('Expected GithubError to be thrown');
} catch (err) {
expect(err).to.be.instanceOf(GithubError);
expect(err.status).to.equal(1);
}
});
});

describe('extract', function () {
it('should reject (not crash the process) when the stream contains invalid gzip data', async () => {
const client = new GitHubClient(GitHubClient.parsePath('contentstack/kickstart-next'));

const badStream = new Readable({ read() {} });
badStream.push(Buffer.from('404: Not Found'));
badStream.push(null);

try {
await client.extract('/tmp', badStream);
Comment thread
cs-raj marked this conversation as resolved.
throw new Error('Expected extraction error to be thrown');
} catch (err) {
expect(err.code).to.equal('Z_DATA_ERROR');
}
});
});
});
Loading
Loading