Merge 14e1242349
into e74ff71f7d
This commit is contained in:
commit
b85eff7468
171
.github/workflows/ci.yml
vendored
171
.github/workflows/ci.yml
vendored
@ -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 }}",
|
||||
})
|
||||
|
60
action.yml
60
action.yml
@ -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
19864
dist/index.js
vendored
File diff suppressed because it is too large
Load Diff
1
dist/index.js.map
vendored
Normal file
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
3910
dist/sourcemap-register.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
8
package-lock.json
generated
8
package-lock.json
generated
@ -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": {
|
||||
|
@ -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",
|
||||
|
131
src/main.ts
131
src/main.ts
@ -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,
|
||||
|
@ -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"
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue
Block a user