Merge pull request #63 from jaredpetersen/master
Added support for GPG signing
This commit is contained in:
commit
1277492cb9
51
README.md
51
README.md
|
@ -29,27 +29,27 @@ Examples of version specifications that the java-version parameter will accept:
|
|||
- A major Java version
|
||||
|
||||
e.g. ```6, 7, 8, 9, 10, 11, 12, 13, ...```
|
||||
|
||||
|
||||
- A semver Java version specification
|
||||
|
||||
e.g. ```8.0.232, 7.0.181, 11.0.4```
|
||||
|
||||
|
||||
e.g. ```8.0.x, >11.0.3, >=13.0.1, <8.0.212```
|
||||
|
||||
|
||||
- An early access (EA) Java version
|
||||
|
||||
e.g. ```14-ea, 15-ea```
|
||||
|
||||
|
||||
e.g. ```14.0.0-ea, 15.0.0-ea```
|
||||
|
||||
|
||||
e.g. ```14.0.0-ea.28, 15.0.0-ea.2``` (syntax for specifying an EA build number)
|
||||
|
||||
|
||||
Note that, per semver rules, EA builds will be matched by explicit EA version specifications.
|
||||
|
||||
|
||||
- 1.x syntax
|
||||
|
||||
e.g. ```1.8``` (same as ```8```)
|
||||
|
||||
|
||||
e.g. ```1.8.0.212``` (same as ```8.0.212```)
|
||||
|
||||
|
||||
|
@ -113,39 +113,60 @@ jobs:
|
|||
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
|
||||
gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} # Value of the GPG private key to import
|
||||
gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase
|
||||
|
||||
- name: Publish to Apache Maven Central
|
||||
run: mvn deploy
|
||||
run: mvn deploy
|
||||
env:
|
||||
MAVEN_USERNAME: maven_username123
|
||||
MAVEN_CENTRAL_TOKEN: ${{ secrets.MAVEN_CENTRAL_TOKEN }}
|
||||
MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
|
||||
```
|
||||
|
||||
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
|
||||
<servers>
|
||||
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd">
|
||||
<servers>
|
||||
<server>
|
||||
<id>github</id>
|
||||
<username>${env.GITHUB_ACTOR}</username>
|
||||
<password>${env.GITHUB_TOKEN}</password>
|
||||
</server>
|
||||
</servers>
|
||||
<server>
|
||||
<id>gpg.passphrase</id>
|
||||
<passphrase>${env.GPG_PASSPHRASE}</passphrase>
|
||||
</server>
|
||||
</servers>
|
||||
</settings>
|
||||
```
|
||||
|
||||
`settings.xml` file created for the second deploy to Apache Maven Central
|
||||
```xml
|
||||
<servers>
|
||||
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd">
|
||||
<servers>
|
||||
<server>
|
||||
<id>maven</id>
|
||||
<username>${env.MAVEN_USERNAME}</username>
|
||||
<password>${env.MAVEN_CENTRAL_TOKEN}</password>
|
||||
</server>
|
||||
</servers>
|
||||
<server>
|
||||
<id>gpg.passphrase</id>
|
||||
<passphrase>${env.MAVEN_GPG_PASSPHRASE}</passphrase>
|
||||
</server>
|
||||
</servers>
|
||||
</settings>
|
||||
```
|
||||
|
||||
***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.***
|
||||
***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.***
|
||||
|
||||
If `gpg-private-key` input is provided, the private key will be written to a file in the runner's temp directory, the private key file will be imported into the GPG keychain, and then the file will be promptly removed before proceeding with the rest of the setup process. A cleanup step will remove the imported private key from the GPG keychain after the job completes regardless of the job status. This ensures that the private key is no longer accessible on self-hosted runners and cannot "leak" between jobs (hosted runners are always clean instances).
|
||||
|
||||
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.
|
||||
|
||||
|
@ -172,7 +193,7 @@ jobs:
|
|||
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`.***
|
||||
***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.
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ describe('auth tests', () => {
|
|||
await io.rmRF(altHome);
|
||||
}, 100000);
|
||||
|
||||
it('creates settings.xml with username and password', async () => {
|
||||
it('creates settings.xml with minimal configuration', async () => {
|
||||
const id = 'packages';
|
||||
const username = 'UNAME';
|
||||
const password = 'TOKEN';
|
||||
|
@ -67,6 +67,21 @@ describe('auth tests', () => {
|
|||
);
|
||||
}, 100000);
|
||||
|
||||
it('creates settings.xml with additional configuration', async () => {
|
||||
const id = 'packages';
|
||||
const username = 'UNAME';
|
||||
const password = 'TOKEN';
|
||||
const gpgPassphrase = 'GPG';
|
||||
|
||||
await auth.configAuthentication(id, username, password, gpgPassphrase);
|
||||
|
||||
expect(fs.existsSync(m2Dir)).toBe(true);
|
||||
expect(fs.existsSync(settingsFile)).toBe(true);
|
||||
expect(fs.readFileSync(settingsFile, 'utf-8')).toEqual(
|
||||
auth.generate(id, username, password, gpgPassphrase)
|
||||
);
|
||||
}, 100000);
|
||||
|
||||
it('overwrites existing settings.xml files', async () => {
|
||||
const id = 'packages';
|
||||
const username = 'USERNAME';
|
||||
|
@ -86,59 +101,50 @@ describe('auth tests', () => {
|
|||
);
|
||||
}, 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', () => {
|
||||
it('generates valid settings.xml with minimal configuration', () => {
|
||||
const id = 'packages';
|
||||
const username = 'USER';
|
||||
const password = '&<>"\'\'"><&';
|
||||
|
||||
expect(auth.generate(id, username, password)).toEqual(`
|
||||
<settings>
|
||||
<servers>
|
||||
<server>
|
||||
<id>${id}</id>
|
||||
<username>\${env.${username}}</username>
|
||||
<password>\${env.&<>"''"><&}</password>
|
||||
</server>
|
||||
</servers>
|
||||
</settings>
|
||||
`);
|
||||
const expectedSettings = `<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd">
|
||||
<servers>
|
||||
<server>
|
||||
<id>${id}</id>
|
||||
<username>\${env.${username}}</username>
|
||||
<password>\${env.&<>"''"><&}</password>
|
||||
</server>
|
||||
</servers>
|
||||
</settings>`;
|
||||
|
||||
expect(auth.generate(id, username, password)).toEqual(expectedSettings);
|
||||
});
|
||||
|
||||
it('generates valid settings.xml with additional configuration', () => {
|
||||
const id = 'packages';
|
||||
const username = 'USER';
|
||||
const password = '&<>"\'\'"><&';
|
||||
const gpgPassphrase = 'PASSPHRASE';
|
||||
|
||||
const expectedSettings = `<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd">
|
||||
<servers>
|
||||
<server>
|
||||
<id>${id}</id>
|
||||
<username>\${env.${username}}</username>
|
||||
<password>\${env.&<>"''"><&}</password>
|
||||
</server>
|
||||
<server>
|
||||
<id>gpg.passphrase</id>
|
||||
<passphrase>\${env.${gpgPassphrase}}</passphrase>
|
||||
</server>
|
||||
</servers>
|
||||
</settings>`;
|
||||
|
||||
expect(auth.generate(id, username, password, gpgPassphrase)).toEqual(
|
||||
expectedSettings
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
import path = require('path');
|
||||
import io = require('@actions/io');
|
||||
import exec = require('@actions/exec');
|
||||
|
||||
jest.mock('@actions/exec', () => {
|
||||
return {
|
||||
exec: jest.fn()
|
||||
};
|
||||
});
|
||||
|
||||
const tempDir = path.join(__dirname, 'runner', 'temp');
|
||||
process.env['RUNNER_TEMP'] = tempDir;
|
||||
|
||||
import gpg = require('../src/gpg');
|
||||
|
||||
describe('gpg tests', () => {
|
||||
beforeEach(async () => {
|
||||
await io.mkdirP(tempDir);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
try {
|
||||
await io.rmRF(tempDir);
|
||||
} catch {
|
||||
console.log('Failed to remove test directories');
|
||||
}
|
||||
});
|
||||
|
||||
describe('importKey', () => {
|
||||
it('attempts to import private key and returns null key id on failure', async () => {
|
||||
const privateKey = 'KEY CONTENTS';
|
||||
const keyId = await gpg.importKey(privateKey);
|
||||
|
||||
expect(keyId).toBeNull();
|
||||
|
||||
expect(exec.exec).toHaveBeenCalledWith(
|
||||
'gpg',
|
||||
expect.anything(),
|
||||
expect.anything()
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteKey', () => {
|
||||
it('deletes private key', async () => {
|
||||
const keyId = 'asdfhjkl';
|
||||
await gpg.deleteKey(keyId);
|
||||
|
||||
expect(exec.exec).toHaveBeenCalledWith(
|
||||
'gpg',
|
||||
expect.anything(),
|
||||
expect.anything()
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,61 @@
|
|||
import path = require('path');
|
||||
|
||||
const env = process.env;
|
||||
|
||||
describe('util tests', () => {
|
||||
beforeEach(() => {
|
||||
const tempEnv = Object.assign({}, env);
|
||||
delete tempEnv.RUNNER_TEMP;
|
||||
delete tempEnv.USERPROFILE;
|
||||
process.env = tempEnv;
|
||||
Object.defineProperty(process, 'platform', {value: 'linux'});
|
||||
});
|
||||
|
||||
describe('getTempDir', () => {
|
||||
it('gets temp dir using env', () => {
|
||||
process.env['RUNNER_TEMP'] = 'defaulttmp';
|
||||
const util = require('../src/util');
|
||||
|
||||
const tempDir = util.getTempDir();
|
||||
|
||||
expect(tempDir).toEqual(process.env['RUNNER_TEMP']);
|
||||
});
|
||||
|
||||
it('gets temp dir for windows using userprofile', () => {
|
||||
Object.defineProperty(process, 'platform', {value: 'win32'});
|
||||
process.env['USERPROFILE'] = 'winusertmp';
|
||||
const util = require('../src/util');
|
||||
|
||||
const tempDir = util.getTempDir();
|
||||
|
||||
expect(tempDir).toEqual(
|
||||
path.join(process.env['USERPROFILE'], 'actions', 'temp')
|
||||
);
|
||||
});
|
||||
|
||||
it('gets temp dir for windows using c drive', () => {
|
||||
Object.defineProperty(process, 'platform', {value: 'win32'});
|
||||
const util = require('../src/util');
|
||||
|
||||
const tempDir = util.getTempDir();
|
||||
|
||||
expect(tempDir).toEqual(path.join('C:\\', 'actions', 'temp'));
|
||||
});
|
||||
|
||||
it('gets temp dir for mac', () => {
|
||||
Object.defineProperty(process, 'platform', {value: 'darwin'});
|
||||
const util = require('../src/util');
|
||||
|
||||
const tempDir = util.getTempDir();
|
||||
|
||||
expect(tempDir).toEqual(path.join('/Users', 'actions', 'temp'));
|
||||
});
|
||||
|
||||
it('gets temp dir for linux', () => {
|
||||
const util = require('../src/util');
|
||||
const tempDir = util.getTempDir();
|
||||
|
||||
expect(tempDir).toEqual(path.join('/home', 'actions', 'temp'));
|
||||
});
|
||||
});
|
||||
});
|
13
action.yml
13
action.yml
|
@ -25,17 +25,27 @@ inputs:
|
|||
description: 'ID of the distributionManagement repository in the pom.xml
|
||||
file. Default is `github`'
|
||||
required: false
|
||||
default: 'github'
|
||||
server-username:
|
||||
description: 'Environment variable name for the username for authentication
|
||||
to the Apache Maven repository. Default is $GITHUB_ACTOR'
|
||||
required: false
|
||||
default: 'GITHUB_ACTOR'
|
||||
server-password:
|
||||
description: 'Environment variable name for password or token for
|
||||
authentication to the Apache Maven repository. Default is $GITHUB_TOKEN'
|
||||
required: false
|
||||
default: 'GITHUB_TOKEN'
|
||||
settings-path:
|
||||
description: 'Path to where the settings.xml file will be written. Default is ~/.m2.'
|
||||
required: false
|
||||
gpg-private-key:
|
||||
description: 'GPG private key to import. Default is empty string.'
|
||||
required: false
|
||||
gpg-passphrase:
|
||||
description: 'Environment variable name for the GPG private key passphrase. Default is
|
||||
$GPG_PASSPHRASE.'
|
||||
required: false
|
||||
outputs:
|
||||
path:
|
||||
description: 'Path to where the java environment has been installed (same as $JAVA_HOME)'
|
||||
|
@ -43,4 +53,5 @@ outputs:
|
|||
description: 'Actual version of the java environment that has been installed'
|
||||
runs:
|
||||
using: 'node12'
|
||||
main: 'dist/index.js'
|
||||
main: 'dist/setup/index.js'
|
||||
post: 'dist/cleanup/index.js'
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -430,6 +430,74 @@
|
|||
"@types/yargs": "^13.0.0"
|
||||
}
|
||||
},
|
||||
"@oozcitak/dom": {
|
||||
"version": "1.15.5",
|
||||
"resolved": "https://registry.npmjs.org/@oozcitak/dom/-/dom-1.15.5.tgz",
|
||||
"integrity": "sha512-L6v3Mwb0TaYBYgeYlIeBaHnc+2ZEaDSbFiRm5KmqZQSoBlbPlf+l6aIH/sD5GUf2MYwULw00LT7+dOnEuAEC0A==",
|
||||
"requires": {
|
||||
"@oozcitak/infra": "1.0.5",
|
||||
"@oozcitak/url": "1.0.0",
|
||||
"@oozcitak/util": "8.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@oozcitak/util": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@oozcitak/util/-/util-8.0.0.tgz",
|
||||
"integrity": "sha512-+9Hq6yuoq/3TRV/n/xcpydGBq2qN2/DEDMqNTG7rm95K6ZE2/YY/sPyx62+1n8QsE9O26e5M1URlXsk+AnN9Jw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@oozcitak/infra": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@oozcitak/infra/-/infra-1.0.5.tgz",
|
||||
"integrity": "sha512-o+zZH7M6l5e3FaAWy3ojaPIVN5eusaYPrKm6MZQt0DKNdgXa2wDYExjpP0t/zx+GoQgQKzLu7cfD8rHCLt8JrQ==",
|
||||
"requires": {
|
||||
"@oozcitak/util": "8.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@oozcitak/util": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@oozcitak/util/-/util-8.0.0.tgz",
|
||||
"integrity": "sha512-+9Hq6yuoq/3TRV/n/xcpydGBq2qN2/DEDMqNTG7rm95K6ZE2/YY/sPyx62+1n8QsE9O26e5M1URlXsk+AnN9Jw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@oozcitak/url": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@oozcitak/url/-/url-1.0.0.tgz",
|
||||
"integrity": "sha512-LGrMeSxeLzsdaitxq3ZmBRVOrlRRQIgNNci6L0VRnOKlJFuRIkNm4B+BObXPCJA6JT5bEJtrrwjn30jueHJYZQ==",
|
||||
"requires": {
|
||||
"@oozcitak/infra": "1.0.3",
|
||||
"@oozcitak/util": "1.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@oozcitak/infra": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@oozcitak/infra/-/infra-1.0.3.tgz",
|
||||
"integrity": "sha512-9O2wxXGnRzy76O1XUxESxDGsXT5kzETJPvYbreO4mv6bqe1+YSuux2cZTagjJ/T4UfEwFJz5ixanOqB0QgYAag==",
|
||||
"requires": {
|
||||
"@oozcitak/util": "1.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@oozcitak/util": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@oozcitak/util/-/util-1.0.1.tgz",
|
||||
"integrity": "sha512-dFwFqcKrQnJ2SapOmRD1nQWEZUtbtIy9Y6TyJquzsalWNJsKIPxmTI0KG6Ypyl8j7v89L2wixH9fQDNrF78hKg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@oozcitak/util": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@oozcitak/util/-/util-1.0.2.tgz",
|
||||
"integrity": "sha512-4n8B1cWlJleSOSba5gxsMcN4tO8KkkcvXhNWW+ADqvq9Xj+Lrl9uCa90GRpjekqQJyt84aUX015DG81LFpZYXA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@oozcitak/util": {
|
||||
"version": "8.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@oozcitak/util/-/util-8.3.3.tgz",
|
||||
"integrity": "sha512-Ufpab7G5PfnEhQyy5kDg9C8ltWJjsVT1P/IYqacjstaqydG4Q21HAT2HUZQYBrC/a1ZLKCz87pfydlDvv8y97w=="
|
||||
},
|
||||
"@types/babel__core": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.3.tgz",
|
||||
|
@ -4955,6 +5023,16 @@
|
|||
"integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==",
|
||||
"dev": true
|
||||
},
|
||||
"xmlbuilder2": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/xmlbuilder2/-/xmlbuilder2-2.1.2.tgz",
|
||||
"integrity": "sha512-PI710tmtVlQ5VmwzbRTuhmVhKnj9pM8Si+iOZCV2g2SNo3gCrpzR2Ka9wNzZtqfD+mnP+xkrqoNy0sjKZqP4Dg==",
|
||||
"requires": {
|
||||
"@oozcitak/dom": "1.15.5",
|
||||
"@oozcitak/infra": "1.0.5",
|
||||
"@oozcitak/util": "8.3.3"
|
||||
}
|
||||
},
|
||||
"y18n": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
"description": "setup java action",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
"build": "ncc build src/setup-java.ts",
|
||||
"build": "ncc build -o dist/setup src/setup-java.ts && ncc build -o dist/cleanup src/cleanup-java.ts",
|
||||
"format": "prettier --write **/*.ts",
|
||||
"format-check": "prettier --check **/*.ts",
|
||||
"prerelease": "npm run-script build",
|
||||
"release": "git add -f dist/index.js",
|
||||
"release": "git add -f dist/setup/index.js dist/cleanup/index.js",
|
||||
"test": "jest"
|
||||
},
|
||||
"repository": {
|
||||
|
@ -29,7 +29,8 @@
|
|||
"@actions/http-client": "^1.0.8",
|
||||
"@actions/io": "^1.0.0",
|
||||
"@actions/tool-cache": "^1.3.1",
|
||||
"semver": "^6.1.1"
|
||||
"semver": "^6.1.1",
|
||||
"xmlbuilder2": "^2.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^24.0.13",
|
||||
|
|
87
src/auth.ts
87
src/auth.ts
|
@ -3,60 +3,73 @@ import * as os from 'os';
|
|||
import * as path from 'path';
|
||||
import * as core from '@actions/core';
|
||||
import * as io from '@actions/io';
|
||||
import {create as xmlCreate} from 'xmlbuilder2';
|
||||
import * as constants from './constants';
|
||||
|
||||
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
|
||||
id: string,
|
||||
username: string,
|
||||
password: string,
|
||||
gpgPassphrase: string | undefined = undefined
|
||||
) {
|
||||
console.log(
|
||||
`creating ${SETTINGS_FILE} with server-id: ${id};`,
|
||||
`environment variables: username=\$${username} and password=\$${password}`
|
||||
'environment variables:',
|
||||
`username=\$${username},`,
|
||||
`password=\$${password},`,
|
||||
`and gpg-passphrase=${gpgPassphrase ? '$' + gpgPassphrase : null}`
|
||||
);
|
||||
// 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
|
||||
const settingsDirectory: string = path.join(
|
||||
core.getInput(constants.INPUT_SETTINGS_PATH) || os.homedir(),
|
||||
core.getInput(constants.INPUT_SETTINGS_PATH) ? '' : M2_DIR
|
||||
);
|
||||
await io.mkdirP(settingsDirectory);
|
||||
core.debug(`created directory ${settingsDirectory}`);
|
||||
await write(
|
||||
settingsDirectory,
|
||||
generate(id, username, password, gpgPassphrase)
|
||||
);
|
||||
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, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
// only exported for testing purposes
|
||||
export function generate(
|
||||
id = DEFAULT_ID,
|
||||
username = DEFAULT_USERNAME,
|
||||
password = DEFAULT_PASSWORD
|
||||
id: string,
|
||||
username: string,
|
||||
password: string,
|
||||
gpgPassphrase: string | undefined = undefined
|
||||
) {
|
||||
return `
|
||||
<settings>
|
||||
<servers>
|
||||
<server>
|
||||
<id>${escapeXML(id)}</id>
|
||||
<username>\${env.${escapeXML(username)}}</username>
|
||||
<password>\${env.${escapeXML(password)}}</password>
|
||||
</server>
|
||||
</servers>
|
||||
</settings>
|
||||
`;
|
||||
const xmlObj: {[key: string]: any} = {
|
||||
settings: {
|
||||
'@xmlns': 'http://maven.apache.org/SETTINGS/1.0.0',
|
||||
'@xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
|
||||
'@xsi:schemaLocation':
|
||||
'http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd',
|
||||
servers: {
|
||||
server: [
|
||||
{
|
||||
id: id,
|
||||
username: `\${env.${username}}`,
|
||||
password: `\${env.${password}}`
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (gpgPassphrase) {
|
||||
const gpgServer = {
|
||||
id: 'gpg.passphrase',
|
||||
passphrase: `\${env.${gpgPassphrase}}`
|
||||
};
|
||||
xmlObj.settings.servers.server.push(gpgServer);
|
||||
}
|
||||
|
||||
return xmlCreate(xmlObj).end({headless: true, prettyPrint: true, width: 80});
|
||||
}
|
||||
|
||||
async function write(directory: string, settings: string) {
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
import * as core from '@actions/core';
|
||||
import * as gpg from './gpg';
|
||||
import * as constants from './constants';
|
||||
|
||||
async function run() {
|
||||
if (core.getInput(constants.INPUT_GPG_PRIVATE_KEY, {required: false})) {
|
||||
core.info('removing private key from keychain');
|
||||
try {
|
||||
const keyFingerprint = core.getState(
|
||||
constants.STATE_GPG_PRIVATE_KEY_FINGERPRINT
|
||||
);
|
||||
await gpg.deleteKey(keyFingerprint);
|
||||
} catch (error) {
|
||||
core.setFailed('failed to remove private key');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
run();
|
|
@ -0,0 +1,16 @@
|
|||
export const INPUT_VERSION = 'version';
|
||||
export const INPUT_JAVA_VERSION = 'java-version';
|
||||
export const INPUT_ARCHITECTURE = 'architecture';
|
||||
export const INPUT_JAVA_PACKAGE = 'java-package';
|
||||
export const INPUT_JDK_FILE = 'jdkFile';
|
||||
export const INPUT_SERVER_ID = 'server-id';
|
||||
export const INPUT_SERVER_USERNAME = 'server-username';
|
||||
export const INPUT_SERVER_PASSWORD = 'server-password';
|
||||
export const INPUT_SETTINGS_PATH = 'settings-path';
|
||||
export const INPUT_GPG_PRIVATE_KEY = 'gpg-private-key';
|
||||
export const INPUT_GPG_PASSPHRASE = 'gpg-passphrase';
|
||||
|
||||
export const INPUT_DEFAULT_GPG_PRIVATE_KEY = undefined;
|
||||
export const INPUT_DEFAULT_GPG_PASSPHRASE = 'GPG_PASSPHRASE';
|
||||
|
||||
export const STATE_GPG_PRIVATE_KEY_FINGERPRINT = 'gpg-private-key-fingerprint';
|
|
@ -0,0 +1,58 @@
|
|||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as io from '@actions/io';
|
||||
import * as exec from '@actions/exec';
|
||||
import * as util from './util';
|
||||
import {ExecOptions} from '@actions/exec/lib/interfaces';
|
||||
|
||||
export const PRIVATE_KEY_FILE = path.join(util.getTempDir(), 'private-key.asc');
|
||||
|
||||
const PRIVATE_KEY_FINGERPRINT_REGEX = /\w{40}/;
|
||||
|
||||
export async function importKey(privateKey: string) {
|
||||
fs.writeFileSync(PRIVATE_KEY_FILE, privateKey, {
|
||||
encoding: 'utf-8',
|
||||
flag: 'w'
|
||||
});
|
||||
|
||||
let output = '';
|
||||
|
||||
const options: ExecOptions = {
|
||||
silent: true,
|
||||
listeners: {
|
||||
stdout: (data: Buffer) => {
|
||||
output += data.toString();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
await exec.exec(
|
||||
'gpg',
|
||||
[
|
||||
'--batch',
|
||||
'--import-options',
|
||||
'import-show',
|
||||
'--import',
|
||||
PRIVATE_KEY_FILE
|
||||
],
|
||||
options
|
||||
);
|
||||
|
||||
await io.rmRF(PRIVATE_KEY_FILE);
|
||||
|
||||
const match = output.match(PRIVATE_KEY_FINGERPRINT_REGEX);
|
||||
return match && match[0];
|
||||
}
|
||||
|
||||
export async function deleteKey(keyFingerprint: string) {
|
||||
await exec.exec(
|
||||
'gpg',
|
||||
['--batch', '--yes', '--delete-secret-keys', keyFingerprint],
|
||||
{silent: true}
|
||||
);
|
||||
await exec.exec(
|
||||
'gpg',
|
||||
['--batch', '--yes', '--delete-keys', keyFingerprint],
|
||||
{silent: true}
|
||||
);
|
||||
}
|
|
@ -1,5 +1,3 @@
|
|||
let tempDirectory = process.env['RUNNER_TEMP'] || '';
|
||||
|
||||
import * as core from '@actions/core';
|
||||
import * as io from '@actions/io';
|
||||
import * as exec from '@actions/exec';
|
||||
|
@ -8,23 +6,10 @@ import * as tc from '@actions/tool-cache';
|
|||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as semver from 'semver';
|
||||
import * as util from './util';
|
||||
|
||||
const IS_WINDOWS = process.platform === 'win32';
|
||||
|
||||
if (!tempDirectory) {
|
||||
let baseLocation;
|
||||
if (IS_WINDOWS) {
|
||||
// On windows use the USERPROFILE env variable
|
||||
baseLocation = process.env['USERPROFILE'] || 'C:\\';
|
||||
} else {
|
||||
if (process.platform === 'darwin') {
|
||||
baseLocation = '/Users';
|
||||
} else {
|
||||
baseLocation = '/home';
|
||||
}
|
||||
}
|
||||
tempDirectory = path.join(baseLocation, 'actions', 'temp');
|
||||
}
|
||||
const tempDirectory = util.getTempDir();
|
||||
const IS_WINDOWS = util.isWindows();
|
||||
|
||||
export async function getJava(
|
||||
version: string,
|
||||
|
|
|
@ -1,30 +1,55 @@
|
|||
import * as core from '@actions/core';
|
||||
import * as installer from './installer';
|
||||
import * as auth from './auth';
|
||||
import * as gpg from './gpg';
|
||||
import * as constants from './constants';
|
||||
import * as path from 'path';
|
||||
|
||||
async function run() {
|
||||
try {
|
||||
let version = core.getInput('version');
|
||||
let version = core.getInput(constants.INPUT_VERSION);
|
||||
if (!version) {
|
||||
version = core.getInput('java-version', {required: true});
|
||||
version = core.getInput(constants.INPUT_JAVA_VERSION, {required: true});
|
||||
}
|
||||
const arch = core.getInput('architecture', {required: true});
|
||||
const javaPackage = core.getInput('java-package', {required: true});
|
||||
const jdkFile = core.getInput('jdkFile', {required: false}) || '';
|
||||
const arch = core.getInput(constants.INPUT_ARCHITECTURE, {required: true});
|
||||
const javaPackage = core.getInput(constants.INPUT_JAVA_PACKAGE, {
|
||||
required: true
|
||||
});
|
||||
const jdkFile = core.getInput(constants.INPUT_JDK_FILE, {required: false});
|
||||
|
||||
await installer.getJava(version, arch, jdkFile, javaPackage);
|
||||
|
||||
const matchersPath = path.join(__dirname, '..', '.github');
|
||||
console.log(`##[add-matcher]${path.join(matchersPath, 'java.json')}`);
|
||||
const matchersPath = path.join(__dirname, '..', '..', '.github');
|
||||
core.info(`##[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;
|
||||
const id = core.getInput(constants.INPUT_SERVER_ID, {required: false});
|
||||
const username = core.getInput(constants.INPUT_SERVER_USERNAME, {
|
||||
required: false
|
||||
});
|
||||
const password = core.getInput(constants.INPUT_SERVER_PASSWORD, {
|
||||
required: false
|
||||
});
|
||||
const gpgPrivateKey =
|
||||
core.getInput(constants.INPUT_GPG_PRIVATE_KEY, {required: false}) ||
|
||||
constants.INPUT_DEFAULT_GPG_PRIVATE_KEY;
|
||||
const gpgPassphrase =
|
||||
core.getInput(constants.INPUT_GPG_PASSPHRASE, {required: false}) ||
|
||||
(gpgPrivateKey ? constants.INPUT_DEFAULT_GPG_PASSPHRASE : undefined);
|
||||
|
||||
await auth.configAuthentication(id, username, password);
|
||||
if (gpgPrivateKey) {
|
||||
core.setSecret(gpgPrivateKey);
|
||||
}
|
||||
|
||||
await auth.configAuthentication(id, username, password, gpgPassphrase);
|
||||
|
||||
if (gpgPrivateKey) {
|
||||
core.info('importing private key');
|
||||
const keyFingerprint = (await gpg.importKey(gpgPrivateKey)) || '';
|
||||
core.saveState(
|
||||
constants.STATE_GPG_PRIVATE_KEY_FINGERPRINT,
|
||||
keyFingerprint
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
core.setFailed(error.message);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
import * as path from 'path';
|
||||
|
||||
export function getTempDir() {
|
||||
let tempDirectory = process.env.RUNNER_TEMP;
|
||||
if (tempDirectory === undefined) {
|
||||
let baseLocation;
|
||||
if (isWindows()) {
|
||||
// On windows use the USERPROFILE env variable
|
||||
baseLocation = process.env['USERPROFILE']
|
||||
? process.env['USERPROFILE']
|
||||
: 'C:\\';
|
||||
} else {
|
||||
if (process.platform === 'darwin') {
|
||||
baseLocation = '/Users';
|
||||
} else {
|
||||
baseLocation = '/home';
|
||||
}
|
||||
}
|
||||
tempDirectory = path.join(baseLocation, 'actions', 'temp');
|
||||
}
|
||||
return tempDirectory;
|
||||
}
|
||||
|
||||
export function isWindows() {
|
||||
return process.platform === 'win32';
|
||||
}
|
Loading…
Reference in New Issue