mirror of
https://github.com/gradle/actions.git
synced 2026-06-14 07:30:41 +00:00
Add resolveAccessKeyForServer for the project-cache trial
Resolves the Develocity access key matching a given server URL, for threading into the new develocityAccessToken cache option (next commit). Reuses DevelocityAccessCredentials.parse and fails closed (returns undefined) when the access key is empty/malformed, the server URL is empty/unparseable, or no key matches the server host. Tolerates a bare hostname with no scheme. Extends short-lived-token.test.ts with coverage for full URLs, bare hostnames, host-only matching (ignoring scheme/port/path), multi-key selection, and the fail-closed cases. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -174,3 +174,22 @@ export class DevelocityAccessCredentials {
|
||||
return this.accessKeyRegexp.test(allKeys)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the access key that matches a given Develocity server, for use as the
|
||||
* `develocityAccessToken` cache option. Returns `undefined` (fail-closed) when the access key is
|
||||
* empty/malformed, the server URL is empty/unparseable, or no key matches the server's host.
|
||||
*/
|
||||
export function resolveAccessKeyForServer(accessKey: string, serverUrl: string): string | undefined {
|
||||
const creds = DevelocityAccessCredentials.parse(accessKey)
|
||||
if (!creds || !serverUrl) {
|
||||
return undefined
|
||||
}
|
||||
let host: string
|
||||
try {
|
||||
host = new URL(serverUrl).hostname
|
||||
} catch {
|
||||
host = serverUrl // tolerate a bare hostname (no scheme)
|
||||
}
|
||||
return creds.keys.find(k => k.hostname === host)?.key
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import nock from "nock";
|
||||
import {describe, expect, it} from '@jest/globals'
|
||||
|
||||
import {DevelocityAccessCredentials, getToken} from "../../src/develocity/short-lived-token";
|
||||
import {DevelocityAccessCredentials, getToken, resolveAccessKeyForServer} from "../../src/develocity/short-lived-token";
|
||||
|
||||
describe('short lived tokens', () => {
|
||||
it('parse valid access key should return an object', async () => {
|
||||
@@ -134,3 +134,37 @@ describe('short lived tokens with retry', () => {
|
||||
.toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('resolveAccessKeyForServer', () => {
|
||||
it('returns the key matching the server host from a full URL', () => {
|
||||
expect(resolveAccessKeyForServer('ge.example.com=key1;other=key2', 'https://ge.example.com')).toBe('key1')
|
||||
})
|
||||
|
||||
it('matches on hostname, ignoring scheme, port and path', () => {
|
||||
expect(resolveAccessKeyForServer('ge.example.com=key1', 'https://ge.example.com:8443/path')).toBe('key1')
|
||||
})
|
||||
|
||||
it('tolerates a bare hostname with no scheme', () => {
|
||||
expect(resolveAccessKeyForServer('ge.example.com=key1', 'ge.example.com')).toBe('key1')
|
||||
})
|
||||
|
||||
it('selects the matching key when multiple are present', () => {
|
||||
expect(resolveAccessKeyForServer('dev=key1;ge.example.com=key2', 'https://ge.example.com')).toBe('key2')
|
||||
})
|
||||
|
||||
it('returns undefined when no key matches the server host', () => {
|
||||
expect(resolveAccessKeyForServer('ge.example.com=key1', 'https://other.example.com')).toBeUndefined()
|
||||
})
|
||||
|
||||
it('returns undefined for an empty server URL', () => {
|
||||
expect(resolveAccessKeyForServer('ge.example.com=key1', '')).toBeUndefined()
|
||||
})
|
||||
|
||||
it('returns undefined for an empty access key', () => {
|
||||
expect(resolveAccessKeyForServer('', 'https://ge.example.com')).toBeUndefined()
|
||||
})
|
||||
|
||||
it('returns undefined for a malformed access key', () => {
|
||||
expect(resolveAccessKeyForServer('not-a-valid-access-key', 'https://ge.example.com')).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user