Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main' into uuid
Browse files Browse the repository at this point in the history
  • Loading branch information
aminya committed Sep 30, 2021
2 parents 3a27dd8 + 27f76df commit ff9d414
Show file tree
Hide file tree
Showing 9 changed files with 22,295 additions and 31 deletions.
22,059 changes: 22,033 additions & 26 deletions package-lock.json

Large diffs are not rendered by default.

53 changes: 53 additions & 0 deletions packages/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@ export interface AnnotationProperties {
*/
title?: string

/**
* The name of the file for which the annotation should be created.
*/
file?: string

/**
* The start line for the annotation.
*/
Expand Down Expand Up @@ -257,3 +262,51 @@ var pid = core.getState("pidToKill");
process.kill(pid);
```

#### OIDC Token

You can use these methods to interact with the GitHub OIDC provider and get a JWT ID token which would help to get access token from third party cloud providers.

**Method Name**: getIDToken()

**Inputs**

audience : optional

**Outputs**

A [JWT](https://jwt.io/) ID Token

In action's `main.ts`:
```js
const core = require('@actions/core');
async function getIDTokenAction(): Promise<void> {
const audience = core.getInput('audience', {required: false})
const id_token1 = await core.getIDToken() // ID Token with default audience
const id_token2 = await core.getIDToken(audience) // ID token with custom audience
// this id_token can be used to get access token from third party cloud providers
}
getIDTokenAction()
```

In action's `actions.yml`:

```yaml
name: 'GetIDToken'
description: 'Get ID token from Github OIDC provider'
inputs:
audience:
description: 'Audience for which the ID token is intended for'
required: false
outputs:
id_token1:
description: 'ID token obtained from OIDC provider'
id_token2:
description: 'ID token obtained from OIDC provider'
runs:
using: 'node12'
main: 'dist/index.js'
```
4 changes: 4 additions & 0 deletions packages/core/RELEASES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# @actions/core Releases

### 1.6.0
- [Added OIDC Client function `getIDToken`](https://github.com/actions/toolkit/pull/919)
- [Added `file` parameter to `AnnotationProperties`](https://github.com/actions/toolkit/pull/896)

### 1.5.0
- [Added support for notice annotations and more annotation fields](https://github.com/actions/toolkit/pull/855)

Expand Down
57 changes: 55 additions & 2 deletions packages/core/__tests__/core.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as fs from 'fs'
import * as os from 'os'
import * as path from 'path'
import * as core from '../src/core'
import {HttpClient} from '@actions/http-client'
import {toCommandProperties} from '../src/utils'

/* eslint-disable @typescript-eslint/unbound-method */
Expand Down Expand Up @@ -274,13 +275,14 @@ describe('@actions/core', () => {
const message = 'this is my error message'
core.error(new Error(message), {
title: 'A title',
file: 'root/test.txt',
startColumn: 1,
endColumn: 2,
startLine: 5,
endLine: 5
})
assertWriteCalls([
`::error title=A title,line=5,endLine=5,col=1,endColumn=2::Error: ${message}${os.EOL}`
`::error title=A title,file=root/test.txt,line=5,endLine=5,col=1,endColumn=2::Error: ${message}${os.EOL}`
])
})

Expand All @@ -304,25 +306,59 @@ describe('@actions/core', () => {
const message = 'this is my error message'
core.warning(new Error(message), {
title: 'A title',
file: 'root/test.txt',
startColumn: 1,
endColumn: 2,
startLine: 5,
endLine: 5
})
assertWriteCalls([
`::warning title=A title,line=5,endLine=5,col=1,endColumn=2::Error: ${message}${os.EOL}`
`::warning title=A title,file=root/test.txt,line=5,endLine=5,col=1,endColumn=2::Error: ${message}${os.EOL}`
])
})

it('notice sets the correct message', () => {
core.notice('Notice')
assertWriteCalls([`::notice::Notice${os.EOL}`])
})

it('notice escapes the message', () => {
core.notice('\r\nnotice\n')
assertWriteCalls([`::notice::%0D%0Anotice%0A${os.EOL}`])
})

it('notice handles an error object', () => {
const message = 'this is my error message'
core.notice(new Error(message))
assertWriteCalls([`::notice::Error: ${message}${os.EOL}`])
})

it('notice handles parameters correctly', () => {
const message = 'this is my error message'
core.notice(new Error(message), {
title: 'A title',
file: 'root/test.txt',
startColumn: 1,
endColumn: 2,
startLine: 5,
endLine: 5
})
assertWriteCalls([
`::notice title=A title,file=root/test.txt,line=5,endLine=5,col=1,endColumn=2::Error: ${message}${os.EOL}`
])
})

it('annotations map field names correctly', () => {
const commandProperties = toCommandProperties({
title: 'A title',
file: 'root/test.txt',
startColumn: 1,
endColumn: 2,
startLine: 5,
endLine: 5
})
expect(commandProperties.title).toBe('A title')
expect(commandProperties.file).toBe('root/test.txt')
expect(commandProperties.col).toBe(1)
expect(commandProperties.endColumn).toBe(2)
expect(commandProperties.line).toBe(5)
Expand Down Expand Up @@ -434,3 +470,20 @@ function verifyFileCommand(command: string, expectedContents: string): void {
fs.unlinkSync(filePath)
}
}

function getTokenEndPoint(): string {
return 'https://vstoken.actions.githubusercontent.com/.well-known/openid-configuration'
}

describe('oidc-client-tests', () => {
it('Get Http Client', async () => {
const http = new HttpClient('actions/oidc-client')
expect(http).toBeDefined()
})

it('HTTP get request to get token endpoint', async () => {
const http = new HttpClient('actions/oidc-client')
const res = await http.get(getTokenEndPoint())
expect(res.message.statusCode).toBe(200)
})
})
52 changes: 50 additions & 2 deletions packages/core/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@actions/core",
"version": "1.5.0",
"version": "1.6.0",
"description": "Actions core lib",
"keywords": [
"github",
Expand Down Expand Up @@ -35,6 +35,9 @@
"bugs": {
"url": "https://github.com/actions/toolkit/issues"
},
"dependencies": {
"@actions/http-client": "^1.0.11"
},
"devDependencies": {
"@types/node": "^12.0.2"
}
Expand Down
11 changes: 11 additions & 0 deletions packages/core/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {toCommandProperties, toCommandValue} from './utils'
import * as os from 'os'
import * as path from 'path'

import {OidcClient} from './oidc-utils'

/**
* Interface for getInput options
*/
Expand Down Expand Up @@ -41,6 +43,11 @@ export interface AnnotationProperties {
*/
title?: string

/**
* The path of the file for which the annotation should be created.
*/
file?: string

/**
* The start line for the annotation.
*/
Expand Down Expand Up @@ -348,3 +355,7 @@ export function saveState(name: string, value: any): void {
export function getState(name: string): string {
return process.env[`STATE_${name}`] || ''
}

export async function getIDToken(aud?: string): Promise<string> {
return await OidcClient.getIDToken(aud)
}
84 changes: 84 additions & 0 deletions packages/core/src/oidc-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/* eslint-disable @typescript-eslint/no-extraneous-class */
import * as actions_http_client from '@actions/http-client'
import {IRequestOptions} from '@actions/http-client/interfaces'
import {HttpClient} from '@actions/http-client'
import {BearerCredentialHandler} from '@actions/http-client/auth'
import {debug, setSecret} from './core'
interface TokenResponse {
value?: string
}

export class OidcClient {
private static createHttpClient(
allowRetry = true,
maxRetry = 10
): actions_http_client.HttpClient {
const requestOptions: IRequestOptions = {
allowRetries: allowRetry,
maxRetries: maxRetry
}

return new HttpClient(
'actions/oidc-client',
[new BearerCredentialHandler(OidcClient.getRequestToken())],
requestOptions
)
}

private static getRequestToken(): string {
const token = process.env['ACTIONS_ID_TOKEN_REQUEST_TOKEN']
if (!token) {
throw new Error(
'Unable to get ACTIONS_ID_TOKEN_REQUEST_TOKEN env variable'
)
}
return token
}

private static getIDTokenUrl(): string {
const runtimeUrl = process.env['ACTIONS_ID_TOKEN_REQUEST_URL']
if (!runtimeUrl) {
throw new Error('Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable')
}
return runtimeUrl
}

private static async getCall(id_token_url: string): Promise<string> {
const httpclient = OidcClient.createHttpClient()

const res = await httpclient
.getJson<TokenResponse>(id_token_url)
.catch(error => {
throw new Error(
`Failed to get ID Token. \n
Error Code : ${error.statusCode}\n
Error Message: ${error.result.message}`
)
})

const id_token = res.result?.value
if (!id_token) {
throw new Error('Response json body do not have ID Token field')
}
return id_token
}

static async getIDToken(audience?: string): Promise<string> {
try {
// New ID Token is requested from action service
let id_token_url: string = OidcClient.getIDTokenUrl()
if (audience) {
const encodedAudience = encodeURIComponent(audience)
id_token_url = `${id_token_url}&audience=${encodedAudience}`
}

debug(`ID token url is ${id_token_url}`)

const id_token = await OidcClient.getCall(id_token_url)
setSecret(id_token)
return id_token
} catch (error) {
throw new Error(`Error message: ${error.message}`)
}
}
}
1 change: 1 addition & 0 deletions packages/core/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export function toCommandProperties(

return {
title: annotationProperties.title,
file: annotationProperties.file,
line: annotationProperties.startLine,
endLine: annotationProperties.endLine,
col: annotationProperties.startColumn,
Expand Down

0 comments on commit ff9d414

Please sign in to comment.