Skip to content
This repository has been archived by the owner on Jul 6, 2022. It is now read-only.

Initial code PR #2

Merged
merged 1 commit into from Mar 7, 2017
Merged

Initial code PR #2

merged 1 commit into from Mar 7, 2017

Conversation

CameronDiver
Copy link
Contributor

@CameronDiver CameronDiver commented Feb 13, 2017

Last PR was closed by GH due to a force push (sorry!) so this PR is actually the previous, with comments addressed.

Last PR: #1

@Page-
Copy link
Contributor

Page- commented Feb 14, 2017

There doesn't appear to be a package.json

@Page-
Copy link
Contributor

Page- commented Feb 14, 2017

Ok, it appears that the package.json with all dependencies included is in the initial commit - it would be nicer to have the dependencies as part of this PR

src/index.ts Outdated
const pack = tar.pack()

return Promise.all(
Promise.resolve(fs.readdir(dirPath))
Copy link
Contributor

Choose a reason for hiding this comment

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

It's probably nicer to use Promise.promisify for this once rather than having to convert the promise every time we call an mz/fs function

src/index.ts Outdated
public buildDir(dirPath: string, buildOpts: Object, hooks: Plugin.IBuildHooks): Promise<NodeJS.ReadableStream> {
const pack = tar.pack()

return Promise.all(
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks like you're doing a Promise.all over a single promise argument? I'm guessing this is either a leftover or something got dropped by accident

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch, that was from when I was mapping over the synchronous fs functions.

src/index.ts Outdated

this.layers = []

if (hooks == null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you can add the default to the func declaration, eg

public createBuildStream(buildOpts: Object, hooks: Plugin.IBuildHooks = {}): NodeJS.ReadWriteStream {

src/index.ts Outdated
if (hook in hooks) {
// Spread the arguments onto the callback function
const fn = hooks[hook]
if (fn !== undefined) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Using _.isFunction(fn) probably makes more sense

src/index.ts Outdated
* @returns {any} The return value of the function, or nothing if the
* function does not exist or does not provide a return value
*/
private callHook = (hooks: Plugin.IBuildHooks, hook: string, ...args: any[]) : any => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the = meant to be in here?

@CameronDiver
Copy link
Contributor Author

Yeah with the package.json I wasn't sure because I wanted it there for @hedss' versionist to merge this PR but wasn't sure going through another PR made any sense. Both ways felt a bit janky tbh.

lib/plugin.d.ts Outdated
* Because of this the minimum recommended registered hooks are buildSuccess and buildFailure,
* but this is not enforced, or required.
*/
export interface IBuildHooks {
Copy link

@hedss hedss Feb 20, 2017

Choose a reason for hiding this comment

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

As @pimterry has pointed out in another review (which I'm really sorry, I can't find), the denoted way to define interfaces is without the I. And in fact, MS recommend you don't:
microsoft/TypeScript-Handbook#121

Personally, I don't mind. But if we prefix I for interfaces, then are we going to use shortened HN elsewhere? I think this probably needs to be specific in the TS spec. (though for some reason I thought I had put it in there).

And sorry this comment is so late!

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, I shouldn't have been listening to the default tslint config. I don't like to prefix them with I anyway :).

Copy link

@hedss hedss left a comment

Choose a reason for hiding this comment

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

TSwise, I think it looks ok (apart from my one comment, which applies in quite a few places).
@pimterry will probably have more. :)

private templateContent: string = ''

public provideEntry(stream: NodeJS.ReadableStream, header: any): boolean {
// Check if this is a file we need to be able to generate the dockerfile
Copy link

Choose a reason for hiding this comment

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

I think the consensus was we were going to allow implicit typing where possible, so as this method always returns a boolean, you won't need it declared in the signature.
Same is true elsewhere.

Choose a reason for hiding this comment

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

Previous discussion: balena-io-modules/balena-procbots#5 (comment). I think the conclusion is:

  • Definitely skip it (do implicit typing) if it's very obvious (let x = new X())
  • Probably type things explicitly otherwise, to taste.

I think method return types are one case though where explicit types are particularly often useful though, and we should be including them. If you explicitly specify this return type, TypeScript checks every path through the method, and guarantees they all always return booleans. Easy to end up with an accidental implicit return or the wrong return type otherwise.

private templateContent: string = ''

public provideEntry(stream: NodeJS.ReadableStream, header: any): boolean {
// Check if this is a file we need to be able to generate the dockerfile

Choose a reason for hiding this comment

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

Previous discussion: balena-io-modules/balena-procbots#5 (comment). I think the conclusion is:

  • Definitely skip it (do implicit typing) if it's very obvious (let x = new X())
  • Probably type things explicitly otherwise, to taste.

I think method return types are one case though where explicit types are particularly often useful though, and we should be including them. If you explicitly specify this return type, TypeScript checks every path through the method, and guarantees they all always return booleans. Easy to end up with an accidental implicit return or the wrong return type otherwise.

console.log(`Image Id: ${imageId}`)
console.log(`Image layers: ${JSON.stringify(layers, null, ' ')}`)
},
buildFailure: (error: Error) : void => {

Choose a reason for hiding this comment

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

Nit: spacing for return types isn't consistent. Compare ) : void here with ): void above. Tiny point, and I don't really mind which way we go, but it'd be nice to pick a single style.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's completely by accident :/ I was going for ): type every time. The example code is being taken out in favour of a nice README anyway, but definitely need to watch out for that.

Copy link

@hedss hedss left a comment

Choose a reason for hiding this comment

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

Given the conversation, LGTM apart from the other two points @pimterry raises.

src/plugin.d.ts Outdated
/**
* Index signature
*/
[key: string]: ((...args: any[]) => any) | undefined

Choose a reason for hiding this comment

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

What's this index signature for? Is it because you want to support arbitrary other new hooks, and you don't know what they are somehow, or is it just because callHook won't compile otherwise?

If the former, fine, if the latter, you should be able to do this more tidily and statically safely with constant string literals. Here's a typescript playground that sort of demonstrates the idea.


// In general we don't want output, until we do.
// call with `env DISPLAY_TEST_OUTPUT=1 npm test` to display output
const dockerPath = process.env.DOCKER_PATH ? process.env.DOCKER_PATH : '/var/run/docker.sock'

Choose a reason for hiding this comment

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

Nit: this could just be process.env.DOCKER_PATH || '/var/run/docker.sock'

Copy link
Contributor

@Page- Page- left a comment

Choose a reason for hiding this comment

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

Can you move the tests from /src/tests to /tests please, I expect /src to only contain actual source code for the module (unless the /src/tests is a typescript convention, @pimterry?)

src/utils.ts Outdated
}

const extractArrowMessage = (message: string): string | undefined => {
const arrowTest = /^\s*-+>\s*(.+)/i
Copy link
Contributor

Choose a reason for hiding this comment

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

There's no need for the i - there's nothing that is affected by case-sensitivity

src/builder.ts Outdated
return fn.apply(null, args)
}
}
return undefined
Copy link
Contributor

Choose a reason for hiding this comment

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

Here you explicity return undefined, but in other places I see you just return, it would be nice to be consistent (personally I prefer return) - is this covered by the TS style guide?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch, yeah I'll need to be consistent with this. The style guide unfortunately isn't ready but it was becoming a blocker. I don't mind going back through and doing a style overhaul, but I want to get the main semantics done.

src/builder.ts Outdated
const relPath = path.join(dirPath, file)
return Promise.all([file, fs.stat(relPath), fs.readFile(relPath)])
})
.map((fileInfo: any[]) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

We know the types for this will be [string, fs.Stats, Buffer] - is there any reason we can't be explicit?

src/builder.ts Outdated
const pack = tar.pack()

return this.readdirBluebird(dirPath)
.map((file: string) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you indent this to show more clearly that it's nested as part of the return statement, rather than chaining directly from it eg with the current indentation I expect return(something).map().map() semantics with a nested chain I would expect return (something.map().map()...) semantics - maybe it's just me but it does feel like a decent styling

A clearer example might be that the current version feels to me a bit like

fn(this.readdirBluebird(dirPath)
.map((file: string) => {
})
.map()
)

src/builder.ts Outdated
*/
public createBuildStream(buildOpts: Object, hooks: Plugin.BuildHooks = {}): NodeJS.ReadWriteStream {

const instance = 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 think it's common to use const self = this for self-references

src/builder.ts Outdated
constructor(dockerOpts: Dockerode.DockerOptions) {
this.docker = new Dockerode(dockerOpts)

// Promisify individual functions, to keep type information
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this meant to be // TODO: ?

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 that's a classic case of comments not keeping up to date with code, nice catch. I actually struggled with getting promisification and TS to work nicely, and this seemed to be the best trade-off.

lib/builder.d.ts Outdated
@@ -0,0 +1,64 @@
/// <reference types="node" />
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this file actually needed? Wouldn't TS dependants just point to the .ts source directly in order to have types direct from the source code (and also be able to use "go to definition", etc), cc @pimterry

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll prefix this comment by saying I've been piecing together the way to package up a TS library from various examples around the web so I'm not 100% that this is the way it should be done.

That said, it seems like the advantage to doing it this way is that the library can be consumed from plain JS, (and therefore all derivatives) and then if the library is being consumed from TS, the compiler will pull in the .d.ts files in addition. It just means AFAIK that changes aren't needed to allow it to work out-of-the-box in both JS and TS.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think you can use:

  "main": "lib/index.js",
  "types": "src/index.ts",

and it should work as I described

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, nice! :)

build.sh Outdated
@@ -0,0 +1,14 @@
#!/bin/bash
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 like to avoid bash-only building, it makes this module a bit trickier to use on windows

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah I'm not keen on that myself, definitely going to change it to something like a gruntfile, I've just never looked into it.

@CameronDiver
Copy link
Contributor Author

CameronDiver commented Feb 28, 2017

The ./src/tests vs ./tests stemmed from the fact that tsc does not like any source files to be below the source root in tsconfig.json, which in this case is ./src. An alternative to the way that I've done it is to add ./tests/ to the exclude section of tsconfig.json and then only build the tests when testing. I have no strong opinion, so if you think that's better then I'll do that. @Page-

package.json Outdated
"test": "echo \"Error: no test specified\" && exit 1"
"start": "",
"build": "./build.sh",
"test": "node_modules/.bin/mocha -r ts-node/register src/tests/tests.ts"
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you change node_modules/.bin/mocha to just mocha, it will be automatically pointed to the correct node_modules path by npm

@Page-
Copy link
Contributor

Page- commented Mar 1, 2017

There's definitely ways to have the test dir at the top level and not have tsc complain (I did that in typed-error, and never saw any error) - I'm guessing it's something to do with this particular set of tsconfig.json options

Copy link
Contributor

@Page- Page- left a comment

Choose a reason for hiding this comment

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

Do the js versions of the tests need to be committed? With the typescript loader I think the tests should be able to be run directly without having to write the js to disk

tsconfig.json Outdated
@@ -12,6 +11,7 @@
},
"exclude": [
"node_modules",
"lib"
"lib",
"tests"
Copy link
Contributor

Choose a reason for hiding this comment

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

Wonky indentation here

package.json Outdated
"description": "A containerised builder which interacts with the docker remote API to perform builds.",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"types": "srcr/index.ts",
Copy link
Contributor

Choose a reason for hiding this comment

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

srcr -> src

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Wow that's really weird, I wonder why the imported types were working from an external module... strange. Anyway, nice catch!

@CameronDiver CameronDiver force-pushed the develop branch 2 times, most recently from fc7158d to 365c437 Compare March 3, 2017 11:58
* Created plugin based architecture which modules users can hook into
* Add tests
* Add gulpfile
* Add README
* Add deps to package.json

Signed-off-by: Cameron Diver <cameron@resin.io>
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants