This commit is contained in:
HarikrishnanBalagopal 2020-12-09 11:42:55 +00:00 committed by GitHub
commit b85eff7468
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 14096 additions and 10068 deletions

View File

@ -9,63 +9,128 @@ jobs:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v2
- run: |
npm install
npm run all
- uses: actions/checkout@v2
- run: |
npm install
npm run all
test: # make sure the action works on a clean machine without building
name: E2E test
# make sure the action works on a clean machine without building
test_1:
name: E2E test on draft release
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v2
- name: Make test pre-release
uses: ./
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: README.md
asset_name: TEST.md
tag: ci-test-${{ matrix.os }}-${{ github.run_id }}
overwrite: true
prerelease: true
body: "rofl lol test"
- name: Check that the uploaded asset is readable
uses: actions/github-script@v2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs')
const child_process = require('child_process');
const assert = require('assert').strict;
- id: get_tag
run: echo "::set-output name=new_tag::ci-test-1-${{ matrix.os }}-${{ github.run_id }}"
- uses: actions/checkout@v2
- id: create_release
name: create temporary release for testing
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.get_tag.outputs.new_tag }}
release_name: Release ${{ steps.get_tag.outputs.new_tag }}
body: |
Changes in this Release
- First Change
- Second Change
draft: true
prerelease: false
- name: Make test pre-release
uses: ./
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ steps.get_tag.outputs.new_tag }}
file: README.md
asset_name: TEST.md
overwrite: true
- name: Check that the uploaded asset is readable
uses: actions/github-script@v2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const assert = require('assert').strict;
const release = await github.repos.getRelease({
...context.repo,
release_id: "${{ steps.create_release.outputs.id }}",
})
assert.deepStrictEqual(release.data.assets[0].name, "TEST.md")
// For a draft release, the `browser_download_url` cannot be used
// since the release hasn't been published yet.
- name: Clean up
if: ${{ always() }}
uses: actions/github-script@v2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
await github.repos.deleteRelease({
...context.repo,
release_id: ${{ steps.create_release.outputs.id }},
})
const expected = fs.readFileSync("README.md")
const release = await github.repos.getReleaseByTag({
...context.repo,
tag: "ci-test-${{ matrix.os }}-${{ github.run_id }}",
})
assert.deepStrictEqual(release.data.prerelease, true)
assert.deepStrictEqual(release.data.body, "rofl lol test")
assert.deepStrictEqual(release.data.assets[0].name, "TEST.md")
const actual = child_process.execSync(`curl -Ls ${release.data.assets[0].browser_download_url}`)
assert.deepStrictEqual(expected, actual)
- name: Clean up
if: ${{ always() }}
uses: actions/github-script@v2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const release = await github.repos.getReleaseByTag({
...context.repo,
tag: "ci-test-${{ matrix.os }}-${{ github.run_id }}",
})
await github.repos.deleteRelease({
...context.repo,
release_id: release.data.id,
})
await github.git.deleteRef({
...context.repo,
ref: "tags/ci-test-${{ matrix.os }}-${{ github.run_id }}",
})
test_2:
name: E2E test on published release
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- id: get_tag
run: echo "::set-output name=new_tag::ci-test-2-${{ matrix.os }}-${{ github.run_id }}"
- uses: actions/checkout@v2
- id: create_release
name: create temporary release for testing
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.get_tag.outputs.new_tag }}
release_name: Release ${{ steps.get_tag.outputs.new_tag }}
body: |
Changes in this Release
- First Change
- Second Change
draft: false
prerelease: false
- name: Make test pre-release
uses: ./
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ steps.get_tag.outputs.new_tag }}
file: README.md
asset_name: TEST.md
overwrite: true
- name: Check that the uploaded asset is readable
uses: actions/github-script@v2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs')
const child_process = require('child_process');
const assert = require('assert').strict;
const expected = fs.readFileSync("README.md")
const release = await github.repos.getReleaseByTag({
...context.repo,
tag: "${{ steps.get_tag.outputs.new_tag }}",
})
assert.deepStrictEqual(release.data.assets[0].name, "TEST.md")
const actual = child_process.execSync(`curl -Ls ${release.data.assets[0].browser_download_url}`)
assert.deepStrictEqual(expected, actual)
- name: Clean up
if: ${{ always() }}
uses: actions/github-script@v2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
await github.repos.deleteRelease({
...context.repo,
release_id: ${{ steps.create_release.outputs.id }},
})
await github.git.deleteRef({
...context.repo,
ref: "tags/${{ steps.get_tag.outputs.new_tag }}",
})

View File

@ -1,36 +1,50 @@
name: 'Upload files to a GitHub release'
description: 'Upload files to a GitHub release (cross-platform)'
author: 'Sven-Hendrik Haase'
name: "Upload files to a GitHub release"
description: "Upload files to a GitHub release (cross-platform)"
author: "Sven-Hendrik Haase"
branding:
icon: archive
color: orange
inputs:
repo_token:
description: 'GitHub token.'
required: true
file:
description: 'Local file to upload.'
required: true
description: "GitHub token."
tag:
description: 'Tag to use as a release.'
required: true
asset_name:
description: 'Name of the asset. When not provided will use the file name. Unused if file_glob is set to "true".'
overwrite:
description: 'Overwrite the release in case it already exists.'
description: "Tag of the release."
file:
required: true
description: |
Path to the file to upload.
If this is a glob pattern then set file_glob to true.
file_glob:
description: 'If true the file can be a glob pattern, asset_name is ignored if this is true.'
prerelease:
description: 'Mark the release as a pre-release. Defaults to "false".'
release_name:
description: 'Explicitly set a release name. Defaults to empty which will cause the release to take the tag as name on GitHub.'
body:
description: 'Content of the release text. Empty by default.'
required: false
description: |
If true the file can be a glob pattern.
asset_name will be ignored if this is true.
default: "false"
asset_name:
required: false
description: |
By default the uploaded asset name will be same as the file name.
Use this to override the asset name. If asset_name contains the
string "$tag", it will get replaced by the release tag.
Unused if file_glob is set to true.
overwrite:
required: false
description: |
By default if an asset already exists with the same name, this action will fail.
Use this to overwrite the asset instead.
default: "false"
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'
required: false
description: |
If the release exists in a different repository then specify its name.
It is required to create a personal access token with `repo`, `user`,
and `admin:repo_hook` scopes to the external repository and give that
as the repo_token.
outputs:
browser_download_url:
description: 'The publicly available URL of the asset.'
description: "The publicly available URL of the asset."
runs:
using: 'node12'
main: 'dist/index.js'
using: "node12"
main: "dist/index.js"

19864
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"
}
},
"@zeit/ncc": {
"version": "0.22.3",
"resolved": "https://registry.npmjs.org/@zeit/ncc/-/ncc-0.22.3.tgz",
"integrity": "sha512-jnCLpLXWuw/PAiJiVbLjA8WBC0IJQbFeUwF4I9M+23MvIxTxk5pD4Q8byQBSPmHQjz5aBoA7AKAElQxMpjrCLQ==",
"@vercel/ncc": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.25.1.tgz",
"integrity": "sha512-dGecC5+1wLof1MQpey4+6i2KZv4Sfs6WfXkl9KfO32GED4ZPiKxRfvtGPjbjZv0IbqMl6CxtcV1RotXYfd5SSA==",
"dev": true
},
"abab": {

View File

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

View File

@ -1,60 +1,82 @@
import * as fs from 'fs'
import {Octokit} from '@octokit/core'
import {Endpoints} from '@octokit/types'
import {
Endpoints,
ReposGetReleaseByTagResponseData,
ReposListReleasesResponseData
} from '@octokit/types'
import * as core from '@actions/core'
import * as github from '@actions/github'
import * as path from 'path'
import * as glob from 'glob'
type GetReleaseByTagResp = Endpoints['GET /repos/:owner/:repo/releases/tags/:tag']['response']
type ListReleasesResp = Endpoints['GET /repos/:owner/:repo/releases']['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 CreateReleaseResp = Endpoints['POST /repos/:owner/:repo/releases']['response']
type UploadAssetResp = Endpoints['POST /repos/:owner/:repo/releases/:release_id/assets{?name,label}']['response']
type ReleaseData =
| ReposGetReleaseByTagResponseData
| ReposListReleasesResponseData[0]
async function get_release_by_tag(
tag: string,
prerelease: boolean,
release_name: string,
body: string,
octokit: Octokit
): Promise<ReleaseByTagResp | CreateReleaseResp> {
): Promise<ReleaseData> {
try {
core.debug(`Getting release by tag ${tag}.`)
return await octokit.repos.getReleaseByTag({
core.info(`Getting release by tag ${tag}`)
const resp: GetReleaseByTagResp = await octokit.repos.getReleaseByTag({
...repo(),
tag: tag
})
core.debug(`response from get release by tag: ${JSON.stringify(resp)}`)
return resp.data
} catch (error) {
// If this returns 404, we need to create the release first.
if (error.status === 404) {
core.debug(
`Release for tag ${tag} doesn't exist yet so we'll create it now.`
)
return await octokit.repos.createRelease({
...repo(),
tag_name: tag,
prerelease: prerelease,
name: release_name,
body: body
})
} else {
if (error.status !== 404) {
core.info(`Failed to get release by tag. Not a 404 error. Throwing.`)
throw error
}
}
// If we get a 404, we need to check the release drafts.
try {
core.info(
'Failed to get release by tag. Checking to see if a release draft with the tag exists.'
)
const resp: ListReleasesResp = await octokit.repos.listReleases(repo())
core.debug(`response from listing releases: ${JSON.stringify(resp)}`)
let found = false
let draftRelease = resp.data[0]
for (const release of resp.data) {
if (release.tag_name === tag) {
draftRelease = release
found = true
break
}
}
if (found) {
core.info('Found release draft with the given tag.')
return draftRelease
}
} catch (error) {
core.info(`Failed to list the releases. Throwing.`)
throw error
}
throw new Error(`No release or release draft found with the tag ${tag}`)
}
async function upload_to_release(
release: ReleaseByTagResp | CreateReleaseResp,
releaseData: ReleaseData,
file: string,
asset_name: string,
tag: string,
overwrite: boolean,
octokit: Octokit
): Promise<undefined | string> {
): Promise<string> {
const stat = fs.statSync(file)
if (!stat.isFile()) {
core.debug(`Skipping ${file}, since its not a file`)
return
core.info(`Skipping ${file} since it is not a file.`)
return ''
}
const file_size = stat.size
const file_bytes = fs.readFileSync(file)
@ -62,32 +84,32 @@ async function upload_to_release(
// Check for duplicates.
const assets: RepoAssetsResp = await octokit.repos.listReleaseAssets({
...repo(),
release_id: release.data.id
release_id: releaseData.id
})
const duplicate_asset = assets.data.find(a => a.name === asset_name)
if (duplicate_asset !== undefined) {
if (overwrite) {
core.debug(
`An asset called ${asset_name} already exists in release ${tag} so we'll overwrite it.`
core.info(
`Overwriting since an asset called ${asset_name} already exists in release ${tag}`
)
await octokit.repos.deleteReleaseAsset({
...repo(),
asset_id: duplicate_asset.id
})
} else {
core.setFailed(`An asset called ${asset_name} already exists.`)
core.setFailed(
`Overwrite is set to false and an asset called ${asset_name} already exists in release ${tag}`
)
return duplicate_asset.browser_download_url
}
} else {
core.debug(
`No pre-existing asset called ${asset_name} found in release ${tag}. All good.`
)
core.info(`Release ${tag} has no pre-existing asset called ${asset_name}`)
}
core.debug(`Uploading ${file} to ${asset_name} in release ${tag}.`)
core.info(`Uploading ${file} to ${asset_name} in release ${tag}`)
const uploaded_asset: UploadAssetResp = await octokit.repos.uploadReleaseAsset(
{
url: release.data.upload_url,
url: releaseData.upload_url,
name: asset_name,
data: file_bytes,
headers: {
@ -102,47 +124,38 @@ async function upload_to_release(
function repo(): {owner: string; repo: string} {
const repo_name = core.getInput('repo_name')
// If we're not targeting a foreign repository, we can just return immediately and don't have to do extra work.
if (!repo_name) {
if (repo_name === '') {
return github.context.repo
}
const owner = repo_name.substr(0, repo_name.indexOf('/'))
if (!owner) {
throw new Error(`Could not extract 'owner' from 'repo_name': ${repo_name}.`)
throw new Error(`Could not extract 'owner' from 'repo_name': ${repo_name}`)
}
const repo = repo_name.substr(repo_name.indexOf('/') + 1)
if (!repo) {
throw new Error(`Could not extract 'repo' from 'repo_name': ${repo_name}.`)
}
return {
owner,
repo
throw new Error(`Could not extract 'repo' from 'repo_name': ${repo_name}`)
}
return {owner, repo}
}
async function run(): Promise<void> {
try {
// Get the inputs from the workflow file: https://github.com/actions/toolkit/tree/master/packages/core#inputsoutputs
const token = core.getInput('repo_token', {required: true})
const file = core.getInput('file', {required: true})
const tag = core
.getInput('tag', {required: true})
.replace('refs/tags/', '')
.replace('refs/heads/', '')
const file_glob = core.getInput('file_glob') == 'true' ? true : false
const overwrite = core.getInput('overwrite') == 'true' ? true : false
const prerelease = core.getInput('prerelease') == 'true' ? true : false
const release_name = core.getInput('release_name')
const body = core.getInput('body')
const file = core.getInput('file', {required: true})
const file_glob = core.getInput('file_glob') === 'true'
const asset_name =
core.getInput('asset_name') === ''
? path.basename(file)
: core.getInput('asset_name').replace(/\$tag/g, tag)
const overwrite = core.getInput('overwrite') === 'true'
const octokit: Octokit = github.getOctokit(token)
const release = await get_release_by_tag(
tag,
prerelease,
release_name,
body,
octokit
)
const release = await get_release_by_tag(tag, octokit)
if (file_glob) {
const files = glob.sync(file)
@ -163,10 +176,6 @@ async function run(): Promise<void> {
core.setFailed('No files matching the glob pattern found.')
}
} else {
const asset_name =
core.getInput('asset_name') !== ''
? core.getInput('asset_name').replace(/\$tag/g, tag)
: path.basename(file)
const asset_download_url = await upload_to_release(
release,
file,

View File

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