2023-11-17 20:04:42 +00:00
import * as core from '@actions/core'
import semver from 'semver'
2024-03-01 16:45:32 +00:00
import * as iaToolkit from '@immutable-actions/toolkit'
import * as attest from '@actions/attest'
import * as cfg from './config'
2023-11-17 20:04:42 +00:00
/**
* The main function for the action.
* @returns {Promise<void>} Resolves when the action is complete.
*/
2024-01-30 14:02:59 +00:00
export async function run () : Promise < void > {
2023-11-17 20:04:42 +00:00
try {
2024-03-01 16:45:32 +00:00
const options : cfg.PublishActionOptions =
await cfg . resolvePublishActionOptions ()
2024-02-06 15:48:26 +00:00
2024-03-01 16:45:32 +00:00
core . info ( `Publishing action package version with options:` )
core . info ( cfg . serializeOptions ( options ))
2024-01-26 13:03:16 -05:00
2024-03-01 16:45:32 +00:00
const semverTag : semver.SemVer = parseSemverTagFromRef ( options . ref )
2024-01-26 13:03:16 -05:00
2024-03-01 16:45:32 +00:00
const stagedActionFilesDir = iaToolkit . createTempDir (
options . runnerTempDir ,
'staging'
)
iaToolkit . stageActionFiles ( options . workspaceDir , stagedActionFilesDir )
2023-11-17 20:04:42 +00:00
2024-03-01 16:45:32 +00:00
const archiveDir = iaToolkit . createTempDir (
options . runnerTempDir ,
'archives'
)
const archives = await iaToolkit . createArchives (
2024-01-29 20:31:00 +00:00
stagedActionFilesDir ,
archiveDir
)
2023-11-17 20:04:42 +00:00
2024-03-01 16:45:32 +00:00
const manifest = iaToolkit . createActionPackageManifest (
2023-11-17 20:04:42 +00:00
archives . tarFile ,
archives . zipFile ,
2024-03-01 16:45:32 +00:00
options . nameWithOwner ,
options . repositoryId ,
options . repositoryOwnerId ,
options . sha ,
semverTag . raw ,
2023-11-17 20:04:42 +00:00
new Date ()
)
2024-03-01 16:45:32 +00:00
const { packageURL , manifestDigest } = await iaToolkit . publishOCIArtifact (
options . token ,
options . containerRegistryUrl ,
options . nameWithOwner ,
semverTag . raw ,
2023-11-17 20:04:42 +00:00
archives . zipFile ,
archives . tarFile ,
2024-02-06 17:30:04 +00:00
manifest
2023-11-17 20:04:42 +00:00
)
core . setOutput ( 'package-url' , packageURL . toString ())
core . setOutput ( 'package-manifest' , JSON . stringify ( manifest ))
2024-01-29 16:44:15 -05:00
core . setOutput ( 'package-manifest-sha' , manifestDigest )
2024-03-01 16:45:32 +00:00
if ( ! options . isEnterprise ) {
const attestation = await generateAttestation (
manifestDigest ,
semverTag . raw ,
options
)
if ( attestation . attestationID !== undefined ) {
core . setOutput ( 'attestation-id' , attestation . attestationID )
}
}
2023-11-17 20:04:42 +00:00
} catch ( error ) {
// Fail the workflow run if an error occurs
2024-01-26 14:49:48 -05:00
if ( error instanceof Error ) core . setFailed ( error . message )
2023-11-17 20:04:42 +00:00
}
}
2024-01-29 20:31:00 +00:00
2024-03-01 16:45:32 +00:00
// This action can be triggered by any workflow that specifies a tag as its GITHUB_REF.
// This includes releases, creating or pushing tags, or workflow_dispatch.
// See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#about-events-that-trigger-workflows.
function parseSemverTagFromRef ( ref : string ) : semver . SemVer {
if ( ! ref . startsWith ( 'refs/tags/' )) {
throw new Error ( `The ref ${ ref } is not a valid tag reference.` )
}
2024-01-29 20:31:00 +00:00
2024-03-01 16:45:32 +00:00
const rawTag = ref . replace ( /^refs\/tags\// , '' )
2024-03-05 16:18:19 +00:00
const semverTag = semver . parse ( rawTag . replace ( /^v/ , '' ))
2024-03-01 16:45:32 +00:00
if ( ! semverTag ) {
2024-01-29 20:31:00 +00:00
throw new Error (
2024-03-01 16:45:32 +00:00
` ${ rawTag } is not a valid semantic version tag, and so cannot be uploaded to the action package.`
2024-01-29 20:31:00 +00:00
)
}
2024-03-01 16:45:32 +00:00
return semverTag
}
// Generate an attestation using the actions toolkit
// Subject name will contain the repo/package name and the tag name
async function generateAttestation (
manifestDigest : string ,
semverTag : string ,
options : cfg.PublishActionOptions
) : Promise < attest.Attestation > {
const subjectName = ` ${ options . nameWithOwner } _ ${ semverTag } `
const subjectDigest = removePrefix ( manifestDigest , 'sha256:' )
return await attest . attestProvenance ({
subjectName ,
subjectDigest : { sha256 : subjectDigest },
token : options.token ,
skipWrite : false // TODO: Attestation storage is only supported for public repositories or repositories which belong to a GitHub Enterprise Cloud account
})
}
2024-01-29 20:31:00 +00:00
2024-03-01 16:45:32 +00:00
function removePrefix ( str : string , prefix : string ) : string {
if ( str . startsWith ( prefix )) {
return str . slice ( prefix . length )
2024-01-29 20:31:00 +00:00
}
2024-03-01 16:45:32 +00:00
return str
2024-01-29 20:31:00 +00:00
}