mirror of
https://github.com/gradle/wrapper-validation-action
synced 2024-11-23 17:22:01 +00:00
First cut
Signed-off-by: Paul Merlin <paul@gradle.com>
This commit is contained in:
parent
0fa564f462
commit
e1189600d2
18 changed files with 736 additions and 204 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -96,4 +96,6 @@ Thumbs.db
|
|||
|
||||
# Ignore built ts files
|
||||
__tests__/runner/*
|
||||
lib/**/*
|
||||
lib/**/*
|
||||
|
||||
.idea
|
||||
|
|
6
__tests__/checksums.test.ts
Normal file
6
__tests__/checksums.test.ts
Normal 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)
|
||||
})
|
0
__tests__/data/invalid/gradle-wrapper.jar
Normal file
0
__tests__/data/invalid/gradle-wrapper.jar
Normal file
BIN
__tests__/data/valid/gradle-wrapper.jar
Normal file
BIN
__tests__/data/valid/gradle-wrapper.jar
Normal file
Binary file not shown.
10
__tests__/find.test.ts
Normal file
10
__tests__/find.test.ts
Normal 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
7
__tests__/hash.test.ts
Normal 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")
|
||||
})
|
|
@ -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())
|
||||
})
|
8
__tests__/validate.test.ts
Normal file
8
__tests__/validate.test.ts
Normal 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")
|
||||
})
|
13
action.yml
13
action.yml
|
@ -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'
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module.exports = {
|
||||
clearMocks: true,
|
||||
moduleFileExtensions: ['js', 'ts'],
|
||||
moduleFileExtensions: ['js', 'ts', 'json'],
|
||||
testEnvironment: 'node',
|
||||
testMatch: ['**/*.test.ts'],
|
||||
testRunner: 'jest-circus/runner',
|
||||
|
@ -8,4 +8,4 @@ module.exports = {
|
|||
'^.+\\.ts$': 'ts-jest'
|
||||
},
|
||||
verbose: true
|
||||
}
|
||||
}
|
||||
|
|
736
package-lock.json
generated
736
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -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
21
src/checksums.ts
Normal 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
31
src/find.ts
Normal 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
12
src/hash.ts
Normal 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))
|
||||
})
|
||||
}
|
24
src/main.ts
24
src/main.ts
|
@ -1,19 +1,19 @@
|
|||
import * as path from "path";
|
||||
import * as core from '@actions/core'
|
||||
import {wait} from './wait'
|
||||
|
||||
async function run(): Promise<void> {
|
||||
try {
|
||||
const ms: string = core.getInput('milliseconds')
|
||||
core.debug(`Waiting ${ms} milliseconds ...`)
|
||||
import * as validate from "./validate"
|
||||
|
||||
core.debug(new Date().toTimeString())
|
||||
await wait(parseInt(ms, 10))
|
||||
core.debug(new Date().toTimeString())
|
||||
|
||||
core.setOutput('time', new Date().toTimeString())
|
||||
} catch (error) {
|
||||
core.setFailed(error.message)
|
||||
}
|
||||
export async function run(): Promise<void> {
|
||||
try {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
run()
|
||||
|
|
21
src/validate.ts
Normal file
21
src/validate.ts
Normal 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 []
|
||||
}
|
|
@ -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)
|
||||
})
|
||||
}
|
Loading…
Reference in a new issue