feat: add support for uploading to draft releases

When we try to get a release by tag it doesn't
return draft releases. In order to get draft
releases we need to list the releases using a
token that has write access on the repo.

Signed-off-by: Harikrishnan Balagopal <harikrishmenon@gmail.com>
This commit is contained in:
Harikrishnan Balagopal 2020-12-08 19:23:13 +05:30
parent e74ff71f7d
commit 8b5823c88f
No known key found for this signature in database
GPG Key ID: 021D78C6DC6A9692
8 changed files with 13938 additions and 9960 deletions

View File

@ -1,36 +1,43 @@
name: 'Upload files to a GitHub release' name: "Upload files to a GitHub release"
description: 'Upload files to a GitHub release (cross-platform)' description: "Upload files to a GitHub release (cross-platform)"
author: 'Sven-Hendrik Haase' author: "Sven-Hendrik Haase"
branding: branding:
icon: archive icon: archive
color: orange color: orange
inputs: inputs:
repo_token: repo_token:
description: 'GitHub token.' description: "GitHub token."
required: true required: true
file: file:
description: 'Local file to upload.' description: "Local file to upload."
required: true required: true
tag: tag:
description: 'Tag to use as a release.' description: "Tag to use as a release."
required: true required: true
asset_name: asset_name:
description: 'Name of the asset. When not provided will use the file name. Unused if file_glob is set to "true".' description: 'Name of the asset. When not provided will use the file name. Unused if file_glob is set to "true".'
required: false
overwrite: overwrite:
description: 'Overwrite the release in case it already exists.' description: "Overwrite the release in case it already exists."
required: false
file_glob: file_glob:
description: 'If true the file can be a glob pattern, asset_name is ignored if this is true.' description: "If true the file can be a glob pattern, asset_name is ignored if this is true."
required: false
prerelease: prerelease:
description: 'Mark the release as a pre-release. Defaults to "false".' description: 'Mark the release as a pre-release. Defaults to "false".'
required: false
release_name: release_name:
description: 'Explicitly set a release name. Defaults to empty which will cause the release to take the tag as name on GitHub.' description: "Explicitly set a release name. Defaults to empty which will cause the release to take the tag as name on GitHub."
required: false
body: body:
description: 'Content of the release text. Empty by default.' description: "Content of the release text. Empty by default."
required: false
repo_name: repo_name:
description: 'Specify the name of the GitHub repository in which the GitHub release will be created, edited, and deleted. If the repository is other than the current, it is required to create a personal access token with `repo`, `user`, `admin:repo_hook` scopes to the foreign repository and add it as a secret. Defaults to the current repository' description: "Specify the name of the GitHub repository in which the GitHub release will be created, edited, and deleted. If the repository is other than the current, it is required to create a personal access token with `repo`, `user`, `admin:repo_hook` scopes to the foreign repository and add it as a secret. Defaults to the current repository"
required: false
outputs: outputs:
browser_download_url: browser_download_url:
description: 'The publicly available URL of the asset.' description: "The publicly available URL of the asset."
runs: runs:
using: 'node12' using: "node12"
main: 'dist/index.js' main: "dist/index.js"

19939
dist/index.js vendored

File diff suppressed because it is too large Load Diff

1
dist/index.js.map vendored Normal file

File diff suppressed because one or more lines are too long

3910
dist/sourcemap-register.js vendored Normal file

File diff suppressed because it is too large Load Diff

8
package-lock.json generated
View File

@ -1691,10 +1691,10 @@
"eslint-visitor-keys": "^1.1.0" "eslint-visitor-keys": "^1.1.0"
} }
}, },
"@zeit/ncc": { "@vercel/ncc": {
"version": "0.22.3", "version": "0.25.1",
"resolved": "https://registry.npmjs.org/@zeit/ncc/-/ncc-0.22.3.tgz", "resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.25.1.tgz",
"integrity": "sha512-jnCLpLXWuw/PAiJiVbLjA8WBC0IJQbFeUwF4I9M+23MvIxTxk5pD4Q8byQBSPmHQjz5aBoA7AKAElQxMpjrCLQ==", "integrity": "sha512-dGecC5+1wLof1MQpey4+6i2KZv4Sfs6WfXkl9KfO32GED4ZPiKxRfvtGPjbjZv0IbqMl6CxtcV1RotXYfd5SSA==",
"dev": true "dev": true
}, },
"abab": { "abab": {

View File

@ -9,8 +9,8 @@
"format": "prettier --write **/*.ts", "format": "prettier --write **/*.ts",
"format-check": "prettier --check **/*.ts", "format-check": "prettier --check **/*.ts",
"lint": "eslint src/**/*.ts", "lint": "eslint src/**/*.ts",
"pack": "ncc build", "pack": "ncc build --source-map -o dist src/main.ts",
"test": "jest", "test": "jest --detectOpenHandles",
"all": "npm run build && npm run format && npm run lint && npm run pack && npm test" "all": "npm run build && npm run format && npm run lint && npm run pack && npm test"
}, },
"repository": { "repository": {
@ -36,7 +36,7 @@
"@types/jest": "^24.9.1", "@types/jest": "^24.9.1",
"@types/node": "^12.12.64", "@types/node": "^12.12.64",
"@typescript-eslint/parser": "^3.5.0", "@typescript-eslint/parser": "^3.5.0",
"@zeit/ncc": "^0.22.3", "@vercel/ncc": "^0.25.1",
"eslint": "^7.10.0", "eslint": "^7.10.0",
"eslint-plugin-github": "^4.0.1", "eslint-plugin-github": "^4.0.1",
"eslint-plugin-jest": "^23.17.1", "eslint-plugin-jest": "^23.17.1",

View File

@ -1,33 +1,61 @@
import * as fs from 'fs' import * as fs from 'fs'
import {Octokit} from '@octokit/core' import {Octokit} from '@octokit/core'
import {Endpoints} from '@octokit/types' import {
Endpoints,
ReposGetReleaseByTagResponseData,
ReposListReleasesResponseData,
ReposCreateReleaseResponseData
} from '@octokit/types'
import * as core from '@actions/core' import * as core from '@actions/core'
import * as github from '@actions/github' import * as github from '@actions/github'
import * as path from 'path' import * as path from 'path'
import * as glob from 'glob' import * as glob from 'glob'
type RepoAssetsResp = Endpoints['GET /repos/:owner/:repo/releases/:release_id/assets']['response'] type RepoAssetsResp = Endpoints['GET /repos/:owner/:repo/releases/:release_id/assets']['response']
type ReleaseByTagResp = Endpoints['GET /repos/:owner/:repo/releases/tags/:tag']['response'] type ListReleasesResp = Endpoints['GET /repos/:owner/:repo/releases']['response']
type CreateReleaseResp = Endpoints['POST /repos/:owner/:repo/releases']['response']
type UploadAssetResp = Endpoints['POST /repos/:owner/:repo/releases/:release_id/assets{?name,label}']['response'] type UploadAssetResp = Endpoints['POST /repos/:owner/:repo/releases/:release_id/assets{?name,label}']['response']
type ReleaseData =
| ReposGetReleaseByTagResponseData
| ReposListReleasesResponseData[0]
| ReposCreateReleaseResponseData
async function get_release_by_tag( async function get_release_by_tag(
tag: string, tag: string,
prerelease: boolean, prerelease: boolean,
release_name: string, release_name: string,
body: string, body: string,
octokit: Octokit octokit: Octokit
): Promise<ReleaseByTagResp | CreateReleaseResp> { ): Promise<ReleaseData> {
try { try {
core.debug(`Getting release by tag ${tag}.`) console.log(`Getting release by tag ${tag}.`)
return await octokit.repos.getReleaseByTag({ return await octokit.repos.getReleaseByTag({
...repo(), ...repo(),
tag: tag tag: tag
}) }).data
} catch (error) { } catch (error) {
// If this returns 404, we need to create the release first. // If this returns 404, we need to create the release first.
if (error.status === 404) { if (error.status === 404) {
core.debug( // The tag could be on a draft release
try {
const listReleasesResp: ListReleasesResp = await octokit.repos.listReleases(
repo()
)
let found = false
let draftRelease = listReleasesResp.data[0]
for (const release of listReleasesResp.data) {
if (release.tag_name === tag) {
draftRelease = release
found = true
break
}
}
if (found) {
return draftRelease
}
} catch (error) {
console.error(`Failed to list the releases. Error:`, error)
}
console.log(
`Release for tag ${tag} doesn't exist yet so we'll create it now.` `Release for tag ${tag} doesn't exist yet so we'll create it now.`
) )
return await octokit.repos.createRelease({ return await octokit.repos.createRelease({
@ -36,7 +64,7 @@ async function get_release_by_tag(
prerelease: prerelease, prerelease: prerelease,
name: release_name, name: release_name,
body: body body: body
}) }).data
} else { } else {
throw error throw error
} }
@ -44,7 +72,7 @@ async function get_release_by_tag(
} }
async function upload_to_release( async function upload_to_release(
release: ReleaseByTagResp | CreateReleaseResp, releaseData: ReleaseData,
file: string, file: string,
asset_name: string, asset_name: string,
tag: string, tag: string,
@ -53,7 +81,7 @@ async function upload_to_release(
): Promise<undefined | string> { ): Promise<undefined | string> {
const stat = fs.statSync(file) const stat = fs.statSync(file)
if (!stat.isFile()) { if (!stat.isFile()) {
core.debug(`Skipping ${file}, since its not a file`) console.log(`Skipping ${file}, since its not a file`)
return return
} }
const file_size = stat.size const file_size = stat.size
@ -62,12 +90,12 @@ async function upload_to_release(
// Check for duplicates. // Check for duplicates.
const assets: RepoAssetsResp = await octokit.repos.listReleaseAssets({ const assets: RepoAssetsResp = await octokit.repos.listReleaseAssets({
...repo(), ...repo(),
release_id: release.data.id release_id: releaseData.id
}) })
const duplicate_asset = assets.data.find(a => a.name === asset_name) const duplicate_asset = assets.data.find(a => a.name === asset_name)
if (duplicate_asset !== undefined) { if (duplicate_asset !== undefined) {
if (overwrite) { if (overwrite) {
core.debug( console.log(
`An asset called ${asset_name} already exists in release ${tag} so we'll overwrite it.` `An asset called ${asset_name} already exists in release ${tag} so we'll overwrite it.`
) )
await octokit.repos.deleteReleaseAsset({ await octokit.repos.deleteReleaseAsset({
@ -79,15 +107,15 @@ async function upload_to_release(
return duplicate_asset.browser_download_url return duplicate_asset.browser_download_url
} }
} else { } else {
core.debug( console.log(
`No pre-existing asset called ${asset_name} found in release ${tag}. All good.` `No pre-existing asset called ${asset_name} found in release ${tag}. All good.`
) )
} }
core.debug(`Uploading ${file} to ${asset_name} in release ${tag}.`) console.log(`Uploading ${file} to ${asset_name} in release ${tag}.`)
const uploaded_asset: UploadAssetResp = await octokit.repos.uploadReleaseAsset( const uploaded_asset: UploadAssetResp = await octokit.repos.uploadReleaseAsset(
{ {
url: release.data.upload_url, url: releaseData.upload_url,
name: asset_name, name: asset_name,
data: file_bytes, data: file_bytes,
headers: { headers: {

View File

@ -2,11 +2,16 @@
"compilerOptions": { "compilerOptions": {
"target": "es6", "target": "es6",
"module": "commonjs", "module": "commonjs",
"moduleResolution": "node",
"outDir": "./lib", "outDir": "./lib",
"rootDir": "./src", "rootDir": "./src",
"strict": true, "strict": true,
"noImplicitAny": true, "noImplicitAny": true,
"esModuleInterop": true "esModuleInterop": true,
"sourceMap": true
}, },
"exclude": ["node_modules", "**/*.test.ts"] "exclude": [
"node_modules",
"**/*.test.ts"
]
} }