Skip to content

Commit

Permalink
rely on dockerfil but support multiple args and ubuntu
Browse files Browse the repository at this point in the history
  • Loading branch information
musdotdigital committed Nov 29, 2023
1 parent be7ace4 commit 47a2fc3
Show file tree
Hide file tree
Showing 13 changed files with 166 additions and 73 deletions.
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ jobs:
- uses: actions/checkout@v3
- uses: ./
with:
dockerfile: ./__tests__/data/Dockerfile
dependencies: ./__tests__/data/dependencies.json
3 changes: 3 additions & 0 deletions __tests__/data/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
FROM alpine:latest

RUN apk add curl
3 changes: 3 additions & 0 deletions __tests__/data/InvalidDockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
FROM alpine:latest

RUN apk-add curl
3 changes: 3 additions & 0 deletions __tests__/data/debianDockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
FROM debian:bullseye-slim

RUN apt-get install
39 changes: 18 additions & 21 deletions __tests__/data/dependencies.json
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
{
"image": "alpine",
"dependencies": [
{
"name": "git",
"version": "2.34.1-r0"
},
{
"name": "make",
"version": "4.3-r0"
},
{
"name": "protoc",
"version": "3.18.1-r1"
},
{
"name": "protobuf-dev",
"version": "3.18.1-r1"
}
]
}
[
{
"name": "git",
"version": "2.34.1-r0"
},
{
"name": "make",
"version": "4.3-r0"
},
{
"name": "protoc",
"version": "3.18.1-r1"
},
{
"name": "protobuf-dev",
"version": "3.18.1-r1"
}
]
23 changes: 10 additions & 13 deletions __tests__/dependencies.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,16 @@ test('save dependencies', async () => {
const pkg = new Package('test', 'latest', {extra: 'field'})
const tmpdir = await mkdtemp(path.join(os.tmpdir(), 'test-save-dep-'))
const outPath = path.join(tmpdir, 'deps.json')
save(outPath, 'alpine', [pkg])
save(outPath, [pkg])

const content = fs.readFileSync(outPath).toString()
const expected = `{
"image": "alpine",
"dependencies": [
{
"name": "test",
"version": "latest",
"extra": "field"
}
]
}`
const expected = `[
{
"name": "test",
"version": "latest",
"extra": "field"
}
]`
expect(content).toBe(expected)
})

Expand All @@ -30,7 +27,7 @@ test('save and load dependencies', async () => {
const depPath = path.join(tmpdir, 'deps.json')
const pkg = new Package('test', 'latest', {extra: 'field'})

save(depPath, 'alpine', [pkg])
const [_, packages] = load(depPath)
save(depPath, [pkg])
const packages = load(depPath)
expect(packages).toEqual([pkg])
})
26 changes: 26 additions & 0 deletions __tests__/dockerfile.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {test, expect} from '@jest/globals'
import * as path from 'path'
import {load} from '../src/dockerfile'
import {AlpineImage, DebImage} from '../src/image'

test('load invalid dockerfile', () => {
let dockerfilePath = path.join(__dirname, 'data', 'InvalidDockerfile')
function loadInvalid() {
load(dockerfilePath)
}
expect(loadInvalid).toThrowError('Unable to extract image from Dockerfile')
})

test('load alpine dockerfile', () => {
const dockerfilePath = path.join(__dirname, 'data', 'Dockerfile')
const dockerfile = load(dockerfilePath)
expect(dockerfile).toBeInstanceOf(AlpineImage)
expect(dockerfile.name).toBe('alpine:latest')
})

test('load debian dockerfile', () => {
const dockerfilePath = path.join(__dirname, 'data', 'debianDockerfile')
const dockerfile = load(dockerfilePath)
expect(dockerfile).toBeInstanceOf(DebImage)
expect(dockerfile.name).toBe('debian:bullseye-slim')
})
4 changes: 4 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
name: 'Dependency updater'
description: 'Update system dependencies of a Dockerfile'
inputs:
dockerfile:
description: 'Dockerfile path'
required: true
default: 'Dockerfile'
dependencies:
description: 'dependencies.json file path'
required: true
Expand Down
84 changes: 67 additions & 17 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.Package = exports.save = exports.load = void 0;
const fs_1 = __importDefault(__nccwpck_require__(147));
const image_1 = __nccwpck_require__(281);
function load(dependencies_path) {
const content = fs_1.default.readFileSync(dependencies_path).toString('utf-8');
const jsonContent = JSON.parse(content);
if (!jsonContent.image || !jsonContent.dependencies) {
throw new Error('Invalid dependencies file');
}
return [
(0, image_1.factory)(jsonContent.image),
packages_from_dict(jsonContent.dependencies)
];
return packages_from_dict(jsonContent);
}
exports.load = load;
function save(dependencies_path, image, dependencies) {
const jsonData = {
image,
dependencies
};
const jsonContent = JSON.stringify(jsonData, null, 2);
function save(dependencies_path, dependencies) {
const jsonContent = JSON.stringify(dependencies, null, 2);
fs_1.default.writeFileSync(dependencies_path, jsonContent);
}
exports.save = save;
Expand All @@ -54,6 +43,63 @@ function packages_from_dict(dict) {
}


/***/ }),

/***/ 149:
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {

"use strict";

var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.load = void 0;
const image = __importStar(__nccwpck_require__(281));
const fs_1 = __importDefault(__nccwpck_require__(147));
function load(dockerfile) {
const content = fs_1.default.readFileSync(dockerfile).toString('utf-8');
return extract_docker_image(content);
}
exports.load = load;
function extract_docker_image(dockerfile_content) {
let imageName = '';
const dockerfileLines = dockerfile_content.split('\n');
for (const line of dockerfileLines) {
if (line.includes('FROM')) {
imageName = line.split(' ')[1].trim();
}
if (line.includes('apk add') || line.includes('apt-get install')) {
return image.factory(imageName);
}
}
throw Error('Unable to extract image from Dockerfile');
}


/***/ }),

/***/ 281:
Expand Down Expand Up @@ -105,7 +151,8 @@ class DebImage extends Image {
let updated_version = undefined;
for (const info of response.raw.split('\n')) {
if (info.includes('Candidate')) {
updated_version = info.split(':')[1].trim();
// must handle case of multiple : in the line i.e. Candidate: 1:8.9p1-3ubuntu0.4
updated_version = info.split(':').slice(1).join(':').trim();
break;
}
}
Expand Down Expand Up @@ -180,12 +227,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
Object.defineProperty(exports, "__esModule", ({ value: true }));
const core = __importStar(__nccwpck_require__(186));
const dependencies = __importStar(__nccwpck_require__(31));
const dockerfile = __importStar(__nccwpck_require__(149));
function run() {
return __awaiter(this, void 0, void 0, function* () {
try {
const dockerfile_path = core.getInput('dockerfile');
const dependencies_path = core.getInput('dependencies');
const apply = core.getBooleanInput('apply');
const [image, dependencies_info] = dependencies.load(dependencies_path);
const image = dockerfile.load(dockerfile_path);
const dependencies_info = dependencies.load(dependencies_path);
const packages_update = dependencies_info.map(function (installed_pkg) {
return __awaiter(this, void 0, void 0, function* () {
return image.get_latest_version(installed_pkg);
Expand All @@ -194,7 +244,7 @@ function run() {
const updated_packages = yield Promise.all(packages_update);
core.exportVariable('updatedDependencies', updated_packages);
if (apply) {
dependencies.save(dependencies_path, image.name, updated_packages);
dependencies.save(dependencies_path, updated_packages);
}
}
catch (error) {
Expand Down
2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

23 changes: 4 additions & 19 deletions src/dependencies.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,13 @@
import fs from 'fs'
import {factory, Image} from './image'

export function load(dependencies_path: string): [Image, Package[]] {
export function load(dependencies_path: string): Package[] {
const content = fs.readFileSync(dependencies_path).toString('utf-8')
const jsonContent = JSON.parse(content)
if (!jsonContent.image || !jsonContent.dependencies) {
throw new Error('Invalid dependencies file')
}
return [
factory(jsonContent.image),
packages_from_dict(jsonContent.dependencies)
]
return packages_from_dict(jsonContent)
}

export function save(
dependencies_path: string,
image: string,
dependencies: Package[]
): void {
const jsonData = {
image,
dependencies
}
const jsonContent = JSON.stringify(jsonData, null, 2)
export function save(dependencies_path: string, dependencies: Package[]): void {
const jsonContent = JSON.stringify(dependencies, null, 2)
fs.writeFileSync(dependencies_path, jsonContent)
}

Expand Down
21 changes: 21 additions & 0 deletions src/dockerfile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import * as image from './image'
import fs from 'fs'

export function load(dockerfile: string): image.Image {
const content = fs.readFileSync(dockerfile).toString('utf-8')
return extract_docker_image(content)
}

function extract_docker_image(dockerfile_content: string): image.Image {
let imageName = ''
const dockerfileLines = dockerfile_content.split('\n')
for (const line of dockerfileLines) {
if (line.includes('FROM')) {
imageName = line.split(' ')[1].trim()
}
if (line.includes('apk add') || line.includes('apt-get install')) {
return image.factory(imageName)
}
}
throw Error('Unable to extract image from Dockerfile')
}
7 changes: 5 additions & 2 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import * as core from '@actions/core'
import * as dependencies from './dependencies'
import * as dockerfile from './dockerfile'

async function run(): Promise<void> {
try {
const dockerfile_path = core.getInput('dockerfile')
const dependencies_path = core.getInput('dependencies')
const apply = core.getBooleanInput('apply')
const [image, dependencies_info] = dependencies.load(dependencies_path)

const image = dockerfile.load(dockerfile_path)
const dependencies_info = dependencies.load(dependencies_path)
const packages_update = dependencies_info.map(async function (
installed_pkg
) {
Expand All @@ -16,7 +19,7 @@ async function run(): Promise<void> {
const updated_packages = await Promise.all(packages_update)
core.exportVariable('updatedDependencies', updated_packages)
if (apply) {
dependencies.save(dependencies_path, image.name, updated_packages)
dependencies.save(dependencies_path, updated_packages)
}
} catch (error) {
if (error instanceof Error) core.setFailed(error.message)
Expand Down

0 comments on commit 47a2fc3

Please sign in to comment.