First cut

Signed-off-by: Paul Merlin <paul@gradle.com>
This commit is contained in:
Paul Merlin 2020-01-05 12:55:59 +01:00
parent 0fa564f462
commit e1189600d2
18 changed files with 736 additions and 204 deletions

2
.gitignore vendored
View file

@ -97,3 +97,5 @@ Thumbs.db
# Ignore built ts files
__tests__/runner/*
lib/**/*
.idea

View file

@ -0,0 +1,6 @@
import * as checksums from '../src/checksums'
test('fetches wrapper jars checksums', async () => {
let validChecksums = await checksums.fetchValidChecksums(false)
expect(validChecksums.length).toBeGreaterThan(10)
})

Binary file not shown.

10
__tests__/find.test.ts Normal file
View file

@ -0,0 +1,10 @@
import * as path from "path";
import * as find from '../src/find'
test('finds test data wrapper jars', async () => {
let repoRoot = path.resolve('.')
let wrapperJars = await find.findWrapperJars(repoRoot)
expect(wrapperJars.length).toBe(2)
expect(wrapperJars).toContain("__tests__/data/valid/gradle-wrapper.jar")
expect(wrapperJars).toContain("__tests__/data/invalid/gradle-wrapper.jar")
})

7
__tests__/hash.test.ts Normal file
View file

@ -0,0 +1,7 @@
import * as path from "path";
import * as hash from '../src/hash'
test('can sha256 files', async () => {
let sha = await hash.sha256File(path.resolve('__tests__/data/invalid/gradle-wrapper.jar'))
expect(sha).toEqual("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
})

View file

@ -1,27 +0,0 @@
import {wait} from '../src/wait'
import * as process from 'process'
import * as cp from 'child_process'
import * as path from 'path'
test('throws invalid number', async () => {
const input = parseInt('foo', 10)
await expect(wait(input)).rejects.toThrow('milliseconds not a number')
})
test('wait 500 ms', async () => {
const start = new Date()
await wait(500)
const end = new Date()
var delta = Math.abs(end.getTime() - start.getTime())
expect(delta).toBeGreaterThan(450)
})
// shows how the runner will run a javascript action with env / stdout protocol
test('test runs', () => {
process.env['INPUT_MILLISECONDS'] = '500'
const ip = path.join(__dirname, '..', 'lib', 'main.js')
const options: cp.ExecSyncOptions = {
env: process.env
}
console.log(cp.execSync(`node ${ip}`, options).toString())
})

View file

@ -0,0 +1,8 @@
import * as path from "path";
import * as validate from '../src/validate'
test('validates wrapper jars', async () => {
let invalidWrapperJars = await validate.findInvalidWrapperJars(path.resolve('.'), false)
expect(invalidWrapperJars.length).toBe(1)
expect(invalidWrapperJars[0]).toEqual("__tests__/data/invalid/gradle-wrapper.jar")
})

View file

@ -1,10 +1,11 @@
name: 'Your name here'
description: 'Provide a description here'
author: 'Your name or organization here'
name: 'Gradle Wrapper Check'
description: 'Check Gradle Wrapper Files'
author: 'Gradle'
inputs:
myInput: # change this
description: 'input description here'
default: 'default value if applicable'
allow-snapshots:
description: 'Allow snapshot Gradle versions'
required: false
default: 'false'
runs:
using: 'node12'
main: 'dist/index.js'

View file

@ -1,6 +1,6 @@
module.exports = {
clearMocks: true,
moduleFileExtensions: ['js', 'ts'],
moduleFileExtensions: ['js', 'ts', 'json'],
testEnvironment: 'node',
testMatch: ['**/*.test.ts'],
testRunner: 'jest-circus/runner',

736
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -25,11 +25,14 @@
"author": "YourNameOrOrganization",
"license": "MIT",
"dependencies": {
"@actions/core": "^1.2.0"
"@actions/core": "^1.2.0",
"@types/nodegit": "^0.26.0",
"nodegit": "^0.26.3",
"typed-rest-client": "^1.7.1"
},
"devDependencies": {
"@types/jest": "^24.0.23",
"@types/node": "^12.7.12",
"@types/node": "^12.12.24",
"@typescript-eslint/parser": "^2.8.0",
"@zeit/ncc": "^0.20.5",
"eslint": "^5.16.0",

21
src/checksums.ts Normal file
View file

@ -0,0 +1,21 @@
import * as httpm from 'typed-rest-client/HttpClient'
const httpc = new httpm.HttpClient("eskatos/gradle-wrapper-check");
export async function fetchValidChecksums(allowSnapshots: boolean): Promise<string[]> {
let all: any[] = await httpGetJson('https://services.gradle.org/versions/all')
let withChecksum = all.filter(entry => entry.hasOwnProperty('wrapperChecksumUrl'))
let allowed = withChecksum.filter(entry => allowSnapshots || !entry.snapshot)
let checksumUrls: string[] = allowed.map(entry => entry.wrapperChecksumUrl)
let checksums = await Promise.all(checksumUrls.map((url: string) => httpGetText(url)))
return [...new Set(checksums)]
}
async function httpGetJson(url: string): Promise<any> {
return JSON.parse(await httpGetText(url));
}
async function httpGetText(url: string): Promise<string> {
const response = await httpc.get(url);
return await response.readBody();
}

31
src/find.ts Normal file
View file

@ -0,0 +1,31 @@
import {Repository, Commit, Tree, TreeEntry} from 'nodegit'
export async function findWrapperJars(gitRepoPath: string): Promise<string[]> {
let repo: Repository = await Repository.open(gitRepoPath)
let commit: Commit = await repo.getMasterCommit()
let tree: Tree = await commit.getTree()
let walker = tree.walk()
let prom: Promise<string[]> = new Promise(((resolve, reject) => {
let wrapperJars: string[] = []
walker.on("entry", (entry: TreeEntry) => {
let path = entry.path()
if (path.endsWith('gradle-wrapper.jar')) {
wrapperJars.push(path)
}
})
walker.on("error", (error) => {
reject(error)
})
walker.on("end", (trees) => {
resolve(wrapperJars)
})
}))
walker.start()
return prom
}

12
src/hash.ts Normal file
View file

@ -0,0 +1,12 @@
import * as crypto from "crypto"
import * as fs from "fs"
export async function sha256File(path: string): Promise<string> {
return new Promise((resolve, reject) => {
const hash = crypto.createHash("sha256")
fs.createReadStream(path)
.on("data", data => hash.update(data))
.on("end", () => resolve(hash.digest("hex")))
.on("error", error => reject(error))
})
}

View file

@ -1,16 +1,16 @@
import * as path from "path";
import * as core from '@actions/core'
import {wait} from './wait'
async function run(): Promise<void> {
import * as validate from "./validate"
export async function run(): Promise<void> {
try {
const ms: string = core.getInput('milliseconds')
core.debug(`Waiting ${ms} milliseconds ...`)
core.debug(new Date().toTimeString())
await wait(parseInt(ms, 10))
core.debug(new Date().toTimeString())
core.setOutput('time', new Date().toTimeString())
const allowSnapshots = core.getInput("allow-snapshots") == "true"
const invalidWrapperJars = await validate.findInvalidWrapperJars(path.resolve('.'), allowSnapshots)
if (invalidWrapperJars.length > 0) {
core.setFailed("Invalid wrapper jars " + invalidWrapperJars)
}
} catch (error) {
core.setFailed(error.message)
}

21
src/validate.ts Normal file
View file

@ -0,0 +1,21 @@
import * as find from "./find"
import * as checksums from "./checksums"
import * as hash from "./hash"
export async function findInvalidWrapperJars(gitRepoRoot: string, allowSnapshots: boolean): Promise<string[]> {
const wrapperJars = await find.findWrapperJars(gitRepoRoot)
if (wrapperJars.length > 0) {
const validChecksums = await checksums.fetchValidChecksums(allowSnapshots)
const invalidWrapperJars: string[] = []
for (let wrapperJar of wrapperJars) {
const sha = await hash.sha256File(wrapperJar)
if (validChecksums.indexOf(sha) < 0) {
invalidWrapperJars.push(wrapperJar)
}
}
return invalidWrapperJars
}
return []
}

View file

@ -1,9 +0,0 @@
export async function wait(milliseconds: number): Promise<string> {
return new Promise(resolve => {
if (isNaN(milliseconds)) {
throw new Error('milliseconds not a number')
}
setTimeout(() => resolve('done!'), milliseconds)
})
}