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

Add push command which starts a build on remote resin servers #868

Merged
2 commits merged into from
May 10, 2018

Conversation

CameronDiver
Copy link
Contributor

@CameronDiver CameronDiver commented Apr 25, 2018

This PR adds a module which coordinates building projects using the resin remote servers. Currently this is meant to have feature parity with git push resin master.

Also added a function in patterns.ts which provides an interface to ask the user a question, and give several options.

Change-type: minor
Connects-to: #843
---- Autogenerated Waffleboard Connection: Connects to #843

export const push: CommandDefinition = {
signature: 'push <application>',
description: 'Start a remote build',
help: 'TODO',
Copy link
Contributor

Choose a reason for hiding this comment

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

This TODO needs TODOING

});

if (applications == null || applications.length === 0) {
throw new Error(`No applications found with name: ${appName}`);
Copy link
Contributor

Choose a reason for hiding this comment

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

This should use exitWithExpectedError (which stops the error being reported to Sentry).

parameter: 'source',
},
],
// FIXME: We shouldn't need to type options here
Copy link
Contributor

Choose a reason for hiding this comment

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

The general way I've been doing this is passing the types as generic args to CommandDefinition. Something like:

export const push: CommandDefinition<{
	application: string 
}, {
	source: string
}> = { 
  ...

That'll then infer the type of param & options here correctly for you.

(I have a beautiful dream that one day I'll find time to rewrite Capitano in a form that makes these types totally inferrable from the definition itself, but not today)


const app: string | null = params.application;
if (app == null) {
throw new Error('You must specify an application');
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be an expected error too.


import { tarDirectory } from './compose';

const DEBUG_MODE = process.env.DEBUG === '1';
Copy link
Contributor

Choose a reason for hiding this comment

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

Everywhere else debug is set with !!process.env.DEBUG - i.e. any set non-empty value.

process.stderr.write('Received SIGINT, cleaning up. Please wait.\n');
cancelBuildIfNecessary(build).then(() => {
stream.end();
process.exit(0);
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be 0? I think 130 is standard for SIGINT.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Given the workflow, I don't think it matters too much, e.g. if we're here, we know we can exit straight away. That being said, 130 is more correct, so I'll change it.

process.removeAllListeners('SIGINT');
process.on('SIGINT', () => {
process.stderr.write('Received SIGINT, cleaning up. Please wait.\n');
cancelBuildIfNecessary(build).then(() => {
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like the builder already checks for closed connections and cancels builds for us: https://github.com/resin-io/resin-builder/blob/92b078f85216d2d62408fde9a3ea87ae400c903c/src/run-build.ts#L173-L180

Can't we use that, instead of reimplementing this client-side too?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Due to the ELB, this doesn't always work. We have extra code in the git server to handle this too, and tbh I think redundancy on something like this is completely fine. If a user ctrl+c's a resin push, the last thing we want is for the build to still get deployed.


async function getRequestStream(build: RemoteBuild): Promise<Stream.Duplex> {
// Tar the directory so that we can send it to the builder
const tarStream = await tarDirectory(build.source);
Copy link
Contributor

Choose a reason for hiding this comment

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

Annoyingly relative paths seem to cause problems with pkg at times e.g. #824.

We need to path.resolve(build.source) somewhere (probably here) to get the correct absolute path and make sure we avoid this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, good to know, thanks!

console.log('[debug] Opened builder connection. Streaming data...');
}

tarStream.pipe(post);
Copy link
Contributor

Choose a reason for hiding this comment

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

Right now, there's no output until the context directory is tarred and fully uploaded. That's not great if you have a large local directory. There was quite a bit of confusion from local push where people didn't understand what it was doing and why it took so long, because they accidentally had enormous local folders they were pushing over the network each time.

Can we add some initial output and a nice spinner while we do the upload?

}

async function cancelBuildIfNecessary(build: RemoteBuild): Promise<void> {
if (build.running && build.releaseId != null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need the separate running flag? Can't we just check if we've got a release id?

@CameronDiver CameronDiver force-pushed the 843-resin-push branch 2 times, most recently from c9cb547 to 3e29cbe Compare May 9, 2018 10:46
@CameronDiver CameronDiver requested a review from pimterry May 9, 2018 10:46
@ghost
Copy link

ghost commented May 9, 2018

@CameronDiver, status checks have failed for this PR. Please make appropriate changes and recommit.

Copy link
Contributor

@pimterry pimterry left a comment

Choose a reason for hiding this comment

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

The build's failing btw because you haven't rebuilt the docs or prettified. npm run build will make sure everything's 100% up to date.

`This command can be used to start a build on the remote resin.io cloud
builders. The given source directory will be added to a tarfile, which
will be sent to the resin.io builder, and the build will proceed. This
can be used as a drop-in replacement for git push to deploy.`;
Copy link
Contributor

Choose a reason for hiding this comment

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

Have you pulled this out just because indentation with backticks is messy? There's a nicer solution to that: stripIndent (e.g. https://github.com/resin-io/resin-cli/blob/b7214a306c6b0a5e45387f93c9142219ff6d7b60/lib/actions/api-key.ts)

import { stripIndent } from 'common-tags';

...

export const push: CommandDefinition<...> = {
	help: stripIndent`
		This command can be used to start a build on the remote resin.io cloud
		builders. The given source directory will be added to a tarfile, which
		will be sent to the resin.io builder, and the build will proceed. This
		can be used as a drop-in replacement for git push to deploy.
	`,

const PUSH_HELP_TEXT =
`This command can be used to start a build on the remote resin.io cloud
builders. The given source directory will be added to a tarfile, which
will be sent to the resin.io builder, and the build will proceed. This
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd skip the tarfile bit - I think a few users won't be familiar with tar files (especially on Windows etc), and it's not strictly necessary. Just will be send to the resin.io builder in a compressed form is fine imo.

exitWithExpectedError(
`No applications found with name: ${appName}.\n\n` +
'This could mean that the application does not exist, or you do \n' +
'not have the permissions required to access it.'
Copy link
Contributor

Choose a reason for hiding this comment

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

You probably want stripIndent here and a multiline string here too.

`This command can be used to start a build on the remote resin.io cloud
builders. The given source directory will be added to a tarfile, which
will be sent to the resin.io builder, and the build will proceed. This
can be used as a drop-in replacement for git push to deploy.`;
Copy link
Contributor

Choose a reason for hiding this comment

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

We also normally include an example command in the help text. This is what becomes the CLI docs on https://docs.resin.io/reference/cli/, and they get nicely rendered there (and they're useful here too).

https://github.com/resin-io/resin-cli/blob/b7214a306c6b0a5e45387f93c9142219ff6d7b60/lib/actions/device.coffee#L133-L143 is a nice example imo.

stream.on('data', (data: BuilderMessage) => {
if (!spinnerStopped) {
uploadSpinner.stop();
spinnerStopped = true;
Copy link
Contributor

Choose a reason for hiding this comment

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

This spinner actually has its own check to make stop() safe: https://github.com/resin-io-modules/resin-cli-visuals/blob/edeb543a5c83cb67b684564f7835e8a69dfeaf6a/lib/widgets/spinner.coffee#L78

You could drop spinnerStopped entirely, and just call uploadSpinner.stop() every time.

// FIXME: Make this error nicer.
console.log(
'Warning: ignoring unknown builder command. You may experience odd build output. ' +
'Maybe you need to update resin-cli?',
Copy link
Contributor

Choose a reason for hiding this comment

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

stripIndent

const uploadSpinner = new visuals.Spinner('Uploading source package to resin cloud');
uploadSpinner.start();

tarStream.pipe(post);
Copy link
Contributor

Choose a reason for hiding this comment

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

In fact, imo we should add a data handler here that's tied to uploadSpinner.stop(), and not return the spinner at all. I think that should work fine, and lets the code everywhere else ignore the spinners entirely.

> = {
signature: 'push <application>',
description: 'Start a remote build on the resin.io cloud build servers',
help: stripIndent`This command can be used to start a build on the remote
Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry, if you test this help text, you'll see this doesn't work properly.

stripIndent strips the common indent, ignoring empty lines, so you'll need to start with an empty line. If you don't, as here, then the common indent is zero (the first line has none) and text all goes wrong.

Same for the other cases here too. You want things like:

	help: stripIndent`
		This command can be used to start a build on the remote
		resin.io cloud builders. The given source directory will be sent to the
		resin.io builder, and the build will proceed. This can be used as a drop-in
		replacement for git push to deploy.
	 
		Examples:
	 
			$ resin push myApp
			$ resin push myApp --source <source directory>
			$ resin push myApp -s <source directory>
	`,

That should do what you expect.

@ghost
Copy link

ghost commented May 10, 2018

@CameronDiver, status checks have failed for this PR. Please make appropriate changes and recommit.

This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants