diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 00000000..f787af87
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+dist/index.js -diff -merge
+dist/index.js linguist-generated=true
diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml
index 4558c6fb..59371d4f 100644
--- a/.github/workflows/workflow.yml
+++ b/.github/workflows/workflow.yml
@@ -1,5 +1,5 @@
name: Main workflow
-on: [push]
+on: [push, pull_request]
jobs:
run:
name: Run
diff --git a/.gitignore b/.gitignore
index 370c1f6e..e6dc0cc5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -93,3 +93,4 @@ typings/
# DynamoDB Local files
.dynamodb/
+.vscode/
diff --git a/README.md b/README.md
index 568fd1be..05b42909 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ This action sets up a java environment for use in actions by:
See [action.yml](action.yml)
-Basic:
+## Basic
```yaml
steps:
- uses: actions/checkout@v1
@@ -25,7 +25,7 @@ steps:
- run: java -cp java HelloWorldApp
```
-From local file:
+## Local file
```yaml
steps:
- uses: actions/checkout@v1
@@ -37,7 +37,7 @@ steps:
- run: java -cp java HelloWorldApp
```
-Matrix Testing:
+## Matrix Testing
```yaml
jobs:
build:
@@ -45,7 +45,7 @@ jobs:
strategy:
matrix:
# test against latest update of each major Java version, as well as specific updates of LTS versions:
- java: [ 1.6, 6.0.83, 7, 7.0.181, 8, 8.0.192, 9.0,x, 10, 11.0.x, 11.0.3, 12, 13 ]
+ java: [ 1.6, 6.0.83, 7, 7.0.181, 8, 8.0.192, 9.0.x, 10, 11.0.x, 11.0.3, 12, 13 ]
name: Java ${{ matrix.java }} sample
steps:
- uses: actions/checkout@master
@@ -56,6 +56,126 @@ jobs:
- run: java -cp java HelloWorldApp
```
+## Publishing using Apache Maven
+```yaml
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v1
+ - name: Set up JDK 1.8
+ uses: actions/setup-java@v1
+ with:
+ java-version: 1.8
+
+ - name: Build with Maven
+ run: mvn -B package --file pom.xml
+
+ - name: Publish to GitHub Packages Apache Maven
+ run: mvn deploy
+ env:
+ GITHUB_TOKEN: ${{ github.token }} # GITHUB_TOKEN is the default env for the password
+
+ - name: Set up Apache Maven Central
+ uses: actions/setup-java@v1
+ with: # running setup-java again overwrites the settings.xml
+ java-version: 1.8
+ server-id: maven # Value of the distributionManagement/repository/id field of the pom.xml
+ server-username: MAVEN_USERNAME # env variable for username in deploy
+ server-password: MAVEN_CENTRAL_TOKEN # env variable for token in deploy
+
+ - name: Publish to Apache Maven Central
+ run: mvn deploy
+ env:
+ MAVEN_USERNAME: maven_username123
+ MAVEN_CENTRAL_TOKEN: ${{ secrets.MAVEN_CENTRAL_TOKEN }}
+```
+
+The two `settings.xml` files created from the above example look like the following.
+
+`settings.xml` file created for the first deploy to GitHub Packages
+```xml
+
+
+ github
+ ${env.GITHUB_ACTOR}
+ ${env.GITHUB_TOKEN}
+
+
+```
+
+`settings.xml` file created for the second deploy to Apache Maven Central
+```xml
+
+
+ maven
+ ${env.MAVEN_USERNAME}
+ ${env.MAVEN_CENTRAL_TOKEN}
+
+
+```
+
+***NOTE: The `settings.xml` file is created in the Actions $HOME directory. If you have an existing `settings.xml` file at that location, it will be overwritten. See below for using the `settings-path` to change your `settings.xml` file location.***
+
+See the help docs on [Publishing a Package](https://help.github.com/en/github/managing-packages-with-github-packages/configuring-apache-maven-for-use-with-github-packages#publishing-a-package) for more information on the `pom.xml` file.
+
+## Publishing using Gradle
+```yaml
+jobs:
+
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v1
+
+ - name: Set up JDK 1.8
+ uses: actions/setup-java@v1
+
+ - name: Build with Gradle
+ run: gradle build
+
+ - name: Publish to GitHub Packages
+ run: gradle publish
+ env:
+ USERNAME: ${{ github.actor }}
+ PASSWORD: ${{ secrets.GITHUB_TOKEN }}
+```
+
+***NOTE: The `USERNAME` and `PASSWORD` need to correspond to the credentials environment variables used in the publishing section of your `build.gradle`.***
+
+See the help docs on [Publishing a Package with Gradle](https://help.github.com/en/github/managing-packages-with-github-packages/configuring-gradle-for-use-with-github-packages#example-using-gradle-groovy-for-a-single-package-in-a-repository) for more information on the `build.gradle` configuration file.
+
+## Apache Maven with a settings path
+
+When using an Actions self-hosted runner with multiple shared runners the default `$HOME` directory can be shared by a number runners at the same time which could overwrite existing settings file. Setting the `settings-path` variable allows you to choose a unique location for your settings file.
+
+```yaml
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v1
+ - name: Set up JDK 1.8 for Shared Runner
+ uses: actions/setup-java@v1
+ with:
+ java-version: 1.8
+ server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
+ settings-path: ${{ github.workspace }} # location for the settings.xml file
+
+ - name: Build with Maven
+ run: mvn -B package --file pom.xml
+
+ - name: Publish to GitHub Packages Apache Maven
+ run: mvn deploy -s $GITHUB_WORKSPACE/settings.xml
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+```
+
# License
The scripts and documentation in this project are released under the [MIT License](LICENSE)
diff --git a/__tests__/auth.test.ts b/__tests__/auth.test.ts
new file mode 100644
index 00000000..13509682
--- /dev/null
+++ b/__tests__/auth.test.ts
@@ -0,0 +1,144 @@
+import io = require('@actions/io');
+import fs = require('fs');
+import os = require('os');
+import path = require('path');
+
+// make the os.homedir() call be local to the tests
+jest.doMock('os', () => {
+ return {
+ homedir: jest.fn(() => __dirname)
+ };
+});
+
+import * as auth from '../src/auth';
+
+const m2Dir = path.join(__dirname, auth.M2_DIR);
+const settingsFile = path.join(m2Dir, auth.SETTINGS_FILE);
+
+describe('auth tests', () => {
+ beforeEach(async () => {
+ await io.rmRF(m2Dir);
+ }, 300000);
+
+ afterAll(async () => {
+ try {
+ await io.rmRF(m2Dir);
+ } catch {
+ console.log('Failed to remove test directories');
+ }
+ }, 100000);
+
+ it('creates settings.xml in alternate locations', async () => {
+ const id = 'packages';
+ const username = 'UNAMI';
+ const password = 'TOLKIEN';
+
+ const altHome = path.join(__dirname, 'runner', 'settings');
+ const altSettingsFile = path.join(altHome, auth.SETTINGS_FILE);
+ process.env[`INPUT_SETTINGS-PATH`] = altHome;
+ await io.rmRF(altHome); // ensure it doesn't already exist
+
+ await auth.configAuthentication(id, username, password);
+
+ expect(fs.existsSync(m2Dir)).toBe(false);
+ expect(fs.existsSync(settingsFile)).toBe(false);
+
+ expect(fs.existsSync(altHome)).toBe(true);
+ expect(fs.existsSync(altSettingsFile)).toBe(true);
+ expect(fs.readFileSync(altSettingsFile, 'utf-8')).toEqual(
+ auth.generate(id, username, password)
+ );
+
+ delete process.env[`INPUT_SETTINGS-PATH`];
+ await io.rmRF(altHome);
+ }, 100000);
+
+ it('creates settings.xml with username and password', async () => {
+ const id = 'packages';
+ const username = 'UNAME';
+ const password = 'TOKEN';
+
+ await auth.configAuthentication(id, username, password);
+
+ expect(fs.existsSync(m2Dir)).toBe(true);
+ expect(fs.existsSync(settingsFile)).toBe(true);
+ expect(fs.readFileSync(settingsFile, 'utf-8')).toEqual(
+ auth.generate(id, username, password)
+ );
+ }, 100000);
+
+ it('overwrites existing settings.xml files', async () => {
+ const id = 'packages';
+ const username = 'USERNAME';
+ const password = 'PASSWORD';
+
+ fs.mkdirSync(m2Dir, {recursive: true});
+ fs.writeFileSync(settingsFile, 'FAKE FILE');
+ expect(fs.existsSync(m2Dir)).toBe(true);
+ expect(fs.existsSync(settingsFile)).toBe(true);
+
+ await auth.configAuthentication(id, username, password);
+
+ expect(fs.existsSync(m2Dir)).toBe(true);
+ expect(fs.existsSync(settingsFile)).toBe(true);
+ expect(fs.readFileSync(settingsFile, 'utf-8')).toEqual(
+ auth.generate(id, username, password)
+ );
+ }, 100000);
+
+ it('does not create settings.xml without required parameters', async () => {
+ await auth.configAuthentication('FOO');
+
+ expect(fs.existsSync(m2Dir)).toBe(true);
+ expect(fs.existsSync(settingsFile)).toBe(true);
+ expect(fs.readFileSync(settingsFile, 'utf-8')).toEqual(
+ auth.generate('FOO', auth.DEFAULT_USERNAME, auth.DEFAULT_PASSWORD)
+ );
+
+ await auth.configAuthentication(undefined, 'BAR', undefined);
+
+ expect(fs.existsSync(m2Dir)).toBe(true);
+ expect(fs.existsSync(settingsFile)).toBe(true);
+ expect(fs.readFileSync(settingsFile, 'utf-8')).toEqual(
+ auth.generate(auth.DEFAULT_ID, 'BAR', auth.DEFAULT_PASSWORD)
+ );
+
+ await auth.configAuthentication(undefined, undefined, 'BAZ');
+
+ expect(fs.existsSync(m2Dir)).toBe(true);
+ expect(fs.existsSync(settingsFile)).toBe(true);
+ expect(fs.readFileSync(settingsFile, 'utf-8')).toEqual(
+ auth.generate(auth.DEFAULT_ID, auth.DEFAULT_USERNAME, 'BAZ')
+ );
+
+ await auth.configAuthentication();
+
+ expect(fs.existsSync(m2Dir)).toBe(true);
+ expect(fs.existsSync(settingsFile)).toBe(true);
+ expect(fs.readFileSync(settingsFile, 'utf-8')).toEqual(
+ auth.generate(
+ auth.DEFAULT_ID,
+ auth.DEFAULT_USERNAME,
+ auth.DEFAULT_PASSWORD
+ )
+ );
+ }, 100000);
+
+ it('escapes invalid XML inputs', () => {
+ const id = 'packages';
+ const username = 'USER';
+ const password = '&<>"\'\'"><&';
+
+ expect(auth.generate(id, username, password)).toEqual(`
+
+
+
+ ${id}
+ \${env.${username}}
+ \${env.&<>"''"><&}
+
+
+
+ `);
+ });
+});
diff --git a/action.yml b/action.yml
index 2bcf1ff4..6337613f 100644
--- a/action.yml
+++ b/action.yml
@@ -1,9 +1,11 @@
name: 'Setup Java JDK'
-description: 'Set up a specific version of the Java JDK and add the command-line tools to the PATH'
+description: 'Set up a specific version of the Java JDK and add the
+ command-line tools to the PATH'
author: 'GitHub'
-inputs:
+inputs:
java-version:
- description: 'The Java version to make available on the path. Takes a whole or semver Java version, or 1.x syntax (e.g. 1.8 => Java 8.x)'
+ description: 'The Java version to make available on the path. Takes a whole
+ or semver Java version, or 1.x syntax (e.g. 1.8 => Java 8.x)'
required: true
java-package:
description: 'The package type (jre, jdk, jdk+fx)'
@@ -14,7 +16,23 @@ inputs:
required: false
default: 'x64'
jdkFile:
- description: 'Path to where the compressed JDK is located. The path could be in your source repository or a local path on the agent.'
+ description: 'Path to where the compressed JDK is located. The path could
+ be in your source repository or a local path on the agent.'
+ required: false
+ server-id:
+ description: 'ID of the distributionManagement repository in the pom.xml
+ file. Default is `github`'
+ required: false
+ server-username:
+ description: 'Environment variable name for the username for authentication
+ to the Apache Maven repository. Default is $GITHUB_ACTOR'
+ required: false
+ server-password:
+ description: 'Environment variable name for password or token for
+ authentication to the Apache Maven repository. Default is $GITHUB_TOKEN'
+ required: false
+ settings-path:
+ description: 'Path to where the settings.xml file will be written. Default is ~/.m2.'
required: false
runs:
using: 'node12'
diff --git a/dist/index.js b/dist/index.js
index 2ec6b325..9e0ab8c4 100644
Binary files a/dist/index.js and b/dist/index.js differ
diff --git a/dist/unzip b/dist/unzip
new file mode 100644
index 00000000..40824180
Binary files /dev/null and b/dist/unzip differ
diff --git a/package-lock.json b/package-lock.json
index f33f89c1..d0a41b10 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2289,9 +2289,9 @@
"dev": true
},
"handlebars": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz",
- "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==",
+ "version": "4.5.3",
+ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz",
+ "integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==",
"dev": true,
"requires": {
"neo-async": "^2.6.0",
diff --git a/package.json b/package.json
index aa088e8f..00922b49 100644
--- a/package.json
+++ b/package.json
@@ -5,10 +5,11 @@
"description": "setup java action",
"main": "dist/index.js",
"scripts": {
- "build": "tsc",
+ "build": "ncc build src/setup-java.ts",
"format": "prettier --write **/*.ts",
"format-check": "prettier --check **/*.ts",
- "release": "ncc build && git add -f dist/",
+ "prerelease": "npm run-script build",
+ "release": "git add -f dist/index.js",
"test": "jest"
},
"repository": {
diff --git a/src/auth.ts b/src/auth.ts
new file mode 100644
index 00000000..2e7c6e8e
--- /dev/null
+++ b/src/auth.ts
@@ -0,0 +1,74 @@
+import * as fs from 'fs';
+import * as os from 'os';
+import * as path from 'path';
+import * as core from '@actions/core';
+import * as io from '@actions/io';
+
+export const M2_DIR = '.m2';
+export const SETTINGS_FILE = 'settings.xml';
+
+export const DEFAULT_ID = 'github';
+export const DEFAULT_USERNAME = 'GITHUB_ACTOR';
+export const DEFAULT_PASSWORD = 'GITHUB_TOKEN';
+
+export async function configAuthentication(
+ id = DEFAULT_ID,
+ username = DEFAULT_USERNAME,
+ password = DEFAULT_PASSWORD
+) {
+ console.log(
+ `creating ${SETTINGS_FILE} with server-id: ${id};`,
+ `environment variables: username=\$${username} and password=\$${password}`
+ );
+ // when an alternate m2 location is specified use only that location (no .m2 directory)
+ // otherwise use the home/.m2/ path
+ const directory: string = path.join(
+ core.getInput('settings-path') || os.homedir(),
+ core.getInput('settings-path') ? '' : M2_DIR
+ );
+ await io.mkdirP(directory);
+ core.debug(`created directory ${directory}`);
+ await write(directory, generate(id, username, password));
+}
+
+function escapeXML(value: string) {
+ return value
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+}
+
+// only exported for testing purposes
+export function generate(
+ id = DEFAULT_ID,
+ username = DEFAULT_USERNAME,
+ password = DEFAULT_PASSWORD
+) {
+ return `
+
+
+
+ ${escapeXML(id)}
+ \${env.${escapeXML(username)}}
+ \${env.${escapeXML(password)}}
+
+
+
+ `;
+}
+
+async function write(directory: string, settings: string) {
+ const location = path.join(directory, SETTINGS_FILE);
+ if (fs.existsSync(location)) {
+ console.warn(`overwriting existing file ${location}`);
+ } else {
+ console.log(`writing ${location}`);
+ }
+
+ return fs.writeFileSync(location, settings, {
+ encoding: 'utf-8',
+ flag: 'w'
+ });
+}
diff --git a/src/installer.ts b/src/installer.ts
index f05171fb..ab4f466b 100644
--- a/src/installer.ts
+++ b/src/installer.ts
@@ -266,8 +266,17 @@ function normalizeVersion(version: string): string {
}
}
- // Add trailing .x if it is missing
- if (version.split('.').length != 3) {
+ if (version.endsWith('-ea')) {
+ // convert e.g. 14-ea to 14.0.0-ea
+ if (version.indexOf('.') == -1) {
+ version = version.slice(0, version.length - 3) + '.0.0-ea';
+ }
+ // match anything in -ea.X (semver won't do .x matching on pre-release versions)
+ if (version[0] >= '0' && version[0] <= '9') {
+ version = '>=' + version;
+ }
+ } else if (version.split('.').length < 3) {
+ // For non-ea versions, add trailing .x if it is missing
if (version[version.length - 1] != 'x') {
version = version + '.x';
}
diff --git a/src/setup-java.ts b/src/setup-java.ts
index 1d26bffe..d0392175 100644
--- a/src/setup-java.ts
+++ b/src/setup-java.ts
@@ -1,5 +1,6 @@
import * as core from '@actions/core';
import * as installer from './installer';
+import * as auth from './auth';
import * as path from 'path';
async function run() {
@@ -16,6 +17,14 @@ async function run() {
const matchersPath = path.join(__dirname, '..', '.github');
console.log(`##[add-matcher]${path.join(matchersPath, 'java.json')}`);
+
+ const id = core.getInput('server-id', {required: false}) || undefined;
+ const username =
+ core.getInput('server-username', {required: false}) || undefined;
+ const password =
+ core.getInput('server-password', {required: false}) || undefined;
+
+ await auth.configAuthentication(id, username, password);
} catch (error) {
core.setFailed(error.message);
}