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: support for multi-stage builds #84

Merged
merged 2 commits into from
Sep 13, 2017
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
18 changes: 13 additions & 5 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,10 @@ module.exports.run = function(configPath, content) {
});

var state = {
instructionsProcessed: 0,
cmdFound: false,
stages: [{
instructionsProcessed: 0,
cmdFound: false // this doesn't seem to be used anywhere
}],
items: [],
rules: loadRules(configPath)
}
Expand All @@ -62,11 +64,17 @@ module.exports.run = function(configPath, content) {

// We care about only having 1 cmd instruction
if (result.command === 'cmd') {
state.cmdFound = true;
state.stages[state.stages.length - 1].cmdFound = true;
} else if (result.command === 'from' && state.stages[state.stages.length - 1].instructionsProcessed !== 0) {
// Each FROM command constitutes a new stage in a multistage build
state.stages.push({
instructionsProcessed: 0,
cmdFound: false
});
}

// And we also care about knowing if this is the first command or not
state.instructionsProcessed = state.instructionsProcessed + 1;
state.stages[state.stages.length - 1].instructionsProcessed++;
};

return state.items;
Expand Down Expand Up @@ -116,7 +124,7 @@ function runLine(state, instructions, idx) {

// check that the first command is a FROM, this might get reported twice, if the FROM command does exist,
// but is not the time (non blank, non commented) line
if ((state.instructionsProcessed === 0 && cmd !== 'from') || (state.instructionsProcessed !== 0 && cmd === 'from')) {
if (state.stages[state.stages.length - 1].instructionsProcessed === 0 && cmd !== 'from') {
items.push(messages.build(state.rules, 'from_first', line));
}

Expand Down
11 changes: 11 additions & 0 deletions test/examples/Dockerfile.multistage
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM golang:1.7.3
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:3.6
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]
11 changes: 11 additions & 0 deletions test/examples/Dockerfile.multistagenamed
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM golang:1.7.3 as builder
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:3.6
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]
16 changes: 10 additions & 6 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@ describe("index", function(){
});
});

describe("#multistage", function(){
it("validates the multistage Dockerfile has no issues", function(){
expect(dockerfilelint.run('./test/examples', fs.readFileSync('./test/examples/Dockerfile.multistage', 'UTF-8'))).to.be.empty;
});

it("validates the multistagenamed Dockerfile has no issues", function(){
expect(dockerfilelint.run('./test/examples', fs.readFileSync('./test/examples/Dockerfile.multistagenamed', 'UTF-8'))).to.be.empty;
});
});

describe("#shell", function() {
it("validates the shell command is accepted when entered correctly", function() {
expect(dockerfilelint.run('./test/examples', fs.readFileSync('./test/examples/Dockerfile.shell.pass', 'UTF-8'))).to.be.empty;
Expand Down Expand Up @@ -91,15 +101,9 @@ describe("index", function(){
{ title: 'First Command Must Be FROM',
rule: 'from_first',
line: 4 },
{ title: 'First Command Must Be FROM',
rule: 'from_first',
line: 5 },
{ title: 'Base Image Missing Tag',
rule: 'missing_tag',
line: 5 },
{ title: 'First Command Must Be FROM',
rule: 'from_first',
line: 6 },
{ title: 'Base Image Latest Tag',
rule: 'latest_tag',
line: 6 },
Expand Down