diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 90218a1..e53f27e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,13 +51,39 @@ jobs: id: checkout uses: actions/checkout@v4 - - name: Test Local Action - id: test-action + - name: Test Local Action (Minimal) + id: test-action-minimal uses: ./ with: - axiom_datasets: ${{ secrets.AXIOM_DATASETS }} - axiom_token: ${{ secrets.AXIOM_TOKEN }} - axiom_org_id: ${{ secrets.AXIOM_ORG_ID }} - title: ${{github.event.ref}} - description: ${{ github.event.head_commit.message }} - url: 'https://example.com' + token: ${{ secrets.AXIOM_TOKEN }} + axiomApiUrl: ${{ secrets.AXIOM_API_URL }} + datasets: annotations-action-ci + type: "minimal-example" + - uses: actions/github-script@v7 + with: + script: | + const assert = require('node:assert/strict') + + const annotationId = '${{ steps.test-action-minimal.outputs.id }}'; + assert.ok(annotationId); + + const res = await fetch(`${{ secrets.AXIOM_API_URL }}/v2/annotations/${annotationId}`, { + headers: { Authorization: `Bearer ${{ secrets.AXIOM_TOKEN }}` } + }); + const annotation = await res.json(); + + assert.equal(annotationId, annotation.id); + assert.equal('minimal-example', annotation.type); + - name: Test Local Action (Complete) + id: test-action-complete + uses: ./ + with: + token: ${{ secrets.AXIOM_TOKEN }} + axiomApiUrl: ${{ secrets.AXIOM_API_URL }} + datasets: annotations-action-ci + time: "2024-01-01T00:00:00Z" + endTime: "2024-01-01T01:00:00Z" + title: "Complete example" + description: "This annotation has all the fields!" + url: "https://axiom.co" + type: "complete-example" diff --git a/action.yml b/action.yml index 1094d10..df48d06 100644 --- a/action.yml +++ b/action.yml @@ -1,32 +1,49 @@ -name: 'Axiom annotations' +name: 'Axiom annotation' +author: 'Axiom, Inc.' description: 'Create annotations in Axiom' -author: 'axiomhq' -# Add your action's branding here. This will appear on the GitHub Marketplace. branding: - icon: 'alert-triangle' + icon: 'message-square' color: 'black' -# Define your inputs here. inputs: - axiom_datasets: - description: 'Comma separated list of datasets to associate the annotations with' + axiomApiUrl: + description: 'The Axiom API URL, defaults to https://api.axiom.co' + required: false + default: '' + datasets: + description: 'Comma separated list of datasets to associate the annotation with' required: true - axiom_token: + token: description: 'Axiom API token' required: true - axiom_org_id: - description: 'Axiom organization ID' - required: true + time: + description: 'The time of the annotation (defaults to now)' + required: false + default: '' + endTime: + description: 'The end time of the annotation (if set, will create a ranged annotation)' + required: false + default: '' title: description: 'The title of the annotation' - required: true + required: false + default: '' description: - description: 'The description of the annotation (markdown supported)' - required: true + description: 'The description of the annotation (supports Markdown)' + required: false + default: '' url: - description: 'The URL to link to (defaults to the GitHub action URL)' + description: 'The URL to link to (defaults to the GitHub Actions run URL)' required: false + default: '' + type: + description: 'The type of the annotation, (e.g. production-deploy)' + required: true + +outputs: + id: + description: 'The ID of the created integration' runs: using: node20 diff --git a/dist/index.js b/dist/index.js index b928d53..fbc0095 100644 --- a/dist/index.js +++ b/dist/index.js @@ -24742,50 +24742,42 @@ const core = __importStar(__nccwpck_require__(2186)); */ async function run() { try { - const datasets = core - .getInput('axiom_datasets', { required: true }) - .split(','); - const token = core.getInput('axiom_token', { required: true }); - const orgId = core.getInput('axiom_org_id', { required: true }); - const title = core.getInput('title', { required: true }); - const description = core.getInput('description', { required: true }); - const url = core.getInput('url', { required: false }) ?? - `https://github.com/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`; - // Debug logs are only output if the `ACTIONS_STEP_DEBUG` secret is true - core.debug('Sending annotation to Axiom'); - const body = { - type: 'deploy', - time: new Date().toISOString(), - datasets, - title, - description, - url + const axiomApiUrl = core.getInput('axiomApiURL') || 'https://api.axiom.co'; + const token = core.getInput('token', { required: true }); + core.setSecret(token); // Mask token in output + const payload = { + datasets: core.getInput('datasets', { required: true }).split(','), + time: core.getInput('time') || new Date().toISOString(), + endTime: core.getInput('endTime') || undefined, + title: core.getInput('title') || undefined, + description: core.getInput('description') || undefined, + url: core.getInput('url') || + `https://github.com/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`, + type: core.getInput('type', { required: true }) }; - // Log the annotation details - core.debug(`Annotation: ${JSON.stringify({ - datasets, - title, - description, - url - })}`); - // Send the annotation to Axiom - const response = await fetch('https://api.axiom.co/v2/annotations', { + core.debug(`Sending annotation to Axiom: ${JSON.stringify(payload)}`); + const response = await fetch(`${axiomApiUrl}/v2/annotations`, { method: 'POST', headers: { 'Content-Type': 'application/json', - Authorization: `Bearer ${token}`, - 'X-Axiom-Org-Id': orgId + Authorization: `Bearer ${token}` }, - body: JSON.stringify(body) + body: JSON.stringify(payload) }); - // Throw an error if the request fails if (!response.ok) { - throw new Error(`Failed to send annotation to Axiom: ${response.statusText}`); + let traceIdMsg = ''; + const traceId = response.headers.get('x-axiom-trace-id'); + if (traceId) { + traceIdMsg = ` (Trace ID: ${traceId})`; + } + throw new Error(`Failed to send annotation to Axiom${traceIdMsg}: ${response.statusText}`); } - core.debug('Annotation sent to Axiom'); + core.debug('Decoding response'); + const annotation = await response.json(); + core.setOutput('id', annotation.id); + core.info(`Created annotation ${annotation.id}`); } catch (error) { - // Fail the workflow run if an error occurs if (error instanceof Error) core.setFailed(error.message); } diff --git a/src/main.ts b/src/main.ts index 59b8370..efc5e11 100644 --- a/src/main.ts +++ b/src/main.ts @@ -6,61 +6,50 @@ import * as core from '@actions/core' */ export async function run(): Promise { try { - const datasets = core - .getInput('axiom_datasets', { required: true }) - .split(',') - const token = core.getInput('axiom_token', { required: true }) - const orgId = core.getInput('axiom_org_id', { required: true }) - const title = core.getInput('title', { required: true }) - const description = core.getInput('description', { required: true }) - const url = - core.getInput('url', { required: false }) ?? - `https://github.com/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}` - - // Debug logs are only output if the `ACTIONS_STEP_DEBUG` secret is true - core.debug('Sending annotation to Axiom') - - const body = { - type: 'deploy', - time: new Date().toISOString(), - - datasets, - title, - description, - url + const axiomApiUrl = core.getInput('axiomApiURL') || 'https://api.axiom.co' + + const token = core.getInput('token', { required: true }) + core.setSecret(token) // Mask token in output + + const payload = { + datasets: core.getInput('datasets', { required: true }).split(','), + time: core.getInput('time') || new Date().toISOString(), + endTime: core.getInput('endTime') || undefined, + title: core.getInput('title') || undefined, + description: core.getInput('description') || undefined, + url: + core.getInput('url') || + `https://github.com/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`, + type: core.getInput('type', { required: true }) } - // Log the annotation details - core.debug( - `Annotation: ${JSON.stringify({ - datasets, - title, - description, - url - })}` - ) - - // Send the annotation to Axiom - const response = await fetch('https://api.axiom.co/v2/annotations', { + core.debug(`Sending annotation to Axiom: ${JSON.stringify(payload)}`) + const response = await fetch(`${axiomApiUrl}/v2/annotations`, { method: 'POST', headers: { 'Content-Type': 'application/json', - Authorization: `Bearer ${token}`, - 'X-Axiom-Org-Id': orgId + Authorization: `Bearer ${token}` }, - body: JSON.stringify(body) + body: JSON.stringify(payload) }) - - // Throw an error if the request fails if (!response.ok) { + let traceIdMsg = '' + const traceId = response.headers.get('x-axiom-trace-id') + if (traceId) { + traceIdMsg = ` (Trace ID: ${traceId})` + } + throw new Error( - `Failed to send annotation to Axiom: ${response.statusText}` + `Failed to send annotation to Axiom${traceIdMsg}: ${response.statusText}` ) } - core.debug('Annotation sent to Axiom') + core.debug('Decoding response') + const annotation = await response.json() + + core.setOutput('id', annotation.id) + core.info(`Created annotation ${annotation.id}`) } catch (error) { - // Fail the workflow run if an error occurs if (error instanceof Error) core.setFailed(error.message) } }