Skip to content

Commit

Permalink
publish symbols & fail on any nuget push errors
Browse files Browse the repository at this point in the history
  • Loading branch information
brandedoutcast committed Apr 19, 2020
1 parent cc84629 commit 0b59dc0
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 107 deletions.
36 changes: 17 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,51 +12,49 @@ on:
- master # Default release branch
jobs:
publish:
name: list on nuget
name: build, pack & push
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2

# Required for a specific dotnet version that doesn't come with ubuntu-latest / windows-latest
# Required in case of previous dotnet SDK versions as the host always has latest version installed
# Visit bit.ly/2synnZl to see the list of SDKs that are pre-installed with ubuntu-latest / windows-latest
# - name: Setup dotnet
# uses: actions/setup-dotnet@v1
# with:
# dotnet-version: 3.1.100

# Publish
- name: publish on version change
uses: rohith/publish-nuget@v2
with:
PROJECT_FILE_PATH: Core/Core.csproj # Relative to repository root
# VERSION_FILE_PATH: Directory.Build.props # Filepath with version info, relative to repository root. Defaults to project file
# PACKAGE_NAME: NuGet package id, REQUIRED if it's different from project name
# VERSION_FILE_PATH: Directory.Build.props # Relative to repository root, defaults to project file
# VERSION_REGEX: <Version>(.*)<\/Version> # Regex pattern to extract version info in a capturing group
# VERSION_STATIC: Bypasses version resolution; useful for external providers like Nerdbank.GitVersioning
# TAG_COMMIT: true # Flag to enable / disalge git tagging
# VERSION_STATIC: Static version, useful for external providers like Nerdbank.GitVersioning
# TAG_COMMIT: true # Flag to enable / disable git tagging
# TAG_FORMAT: v* # Format of the git tag, [*] gets replaced with version
# NUGET_KEY: ${{secrets.NUGET_API_KEY}} # nuget.org API key
# PACKAGE_NAME: NuGet package name, required when it's different from project name. Defaults to project name
# NUGET_KEY: ${{secrets.NUGET_API_KEY}} # API key for the NuGet feed
```

- With all settings on default, updates to project version are monitored on every push / PR merge to master & a new tag is created
- If a `NUGET_KEY` is present then the project gets built, packed & published to nuget.org
- Project gets built, packed & published only if there's a `NUGET_KEY` configured in the repository

## Inputs
Most of the inputs are optional

Input | Default Value | Description
--- | --- | ---
PROJECT_FILE_PATH | | File path of the project to be packaged, relative to repository root
VERSION_FILE_PATH | `[PROJECT_FILE_PATH]` | File path containing version info, relative to repository root
PROJECT_FILE_PATH | | Filepath of the project to be packaged, relative to root of repository
PACKAGE_NAME | | NuGet package id to check against version changes, defaults to project name
VERSION_FILE_PATH | `[PROJECT_FILE_PATH]` | Filepath containing version info, relative to root of repository
VERSION_REGEX | `<Version>(.*)<\/Version>` | Regex pattern to extract version info in a capturing group
VERSION_STATIC| | Bypasses version resolution; useful for external providers like Nerdbank.GitVersioning
VERSION_STATIC| | Static version, useful for external providers like Nerdbank.GitVersioning
TAG_COMMIT | `true` | Flag to enable / disable git tagging
TAG_FORMAT | `v*` | `[*]` is a placeholder for the actual project version
NUGET_KEY | | API key to authorize the package upload to nuget.org
PACKAGE_NAME | | Name of the NuGet package, required when it's different from project name
TAG_FORMAT | `v*` | Format of the git tag, `[*]` gets replaced with version
NUGET_KEY | | API key for the NuGet feed

**Note:**
For multiple projects, every input except `PROJECT_FILE_PATH` can be given as `env` variable at [job / workflow level](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#env)
**Note:**
Multiple projects can make use of steps to configure each project individually, common inputs between steps can be given as `env` for [job / workflow](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#env)

## License
[MIT](LICENSE)
18 changes: 9 additions & 9 deletions action.yml
Original file line number Diff line number Diff line change
@@ -1,35 +1,35 @@
name: Publish NuGet
author: Rohith Reddy (@rohith)
description: Build, Pack & Publish NuGet packages with dotnet (.net) core on project version change
description: Build, Pack & Publish a NuGet package with dotnet core on project version change

inputs:
PROJECT_FILE_PATH:
description: Filepath of the project to be packaged, relative to root of repository
required: true
PACKAGE_NAME:
description: NuGet package id to check against version changes, defaults to project name
required: false
VERSION_FILE_PATH:
description: Filepath containing version info, relative to root of repository. Defaults to `PROJECT_FILE_PATH` if not specified
description: Filepath containing version info, relative to root of repository
required: false
VERSION_REGEX:
description: Regex (with version in a capturing group) to extract the version from `VERSION_FILE_PATH`
description: Regex pattern to extract version info in a capturing group
required: false
default: <Version>(.*)<\/Version>
VERSION_STATIC:
description: Bypasses version resolution; useful for external providers like Nerdbank.GitVersioning
description: Static version, useful for external providers like Nerdbank.GitVersioning
required: false
TAG_COMMIT:
description: Whether to create a tag when there's a version change
description: Flag to enable / disable git tagging
required: false
default: true
TAG_FORMAT:
description: Format of the tag, `*` is the placeholder for actual version
description: Format of the git tag, `[*]` gets replaced with version
required: false
default: v*
NUGET_KEY:
description: API key for the NuGet feed
required: false
PACKAGE_NAME:
description: NuGet package name, required when it's different from the project name
required: false

runs:
using: node12
Expand Down
152 changes: 73 additions & 79 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,144 +5,138 @@ const path = require("path"),

class Action {
constructor() {
this.PROJECT_FILE_PATH = process.env.INPUT_PROJECT_FILE_PATH
this.VERSION_FILE_PATH = process.env.INPUT_VERSION_FILE_PATH || process.env.VERSION_FILE_PATH
this.VERSION_REGEX = new RegExp(process.env.INPUT_VERSION_REGEX || process.env.VERSION_REGEX)
this.VERSION_STATIC = process.env.INPUT_VERSION_STATIC || process.env.VERSION_STATIC
this.TAG_COMMIT = JSON.parse(process.env.INPUT_TAG_COMMIT || process.env.TAG_COMMIT)
this.TAG_FORMAT = process.env.INPUT_TAG_FORMAT || process.env.TAG_FORMAT
this.NUGET_KEY = process.env.INPUT_NUGET_KEY || process.env.NUGET_KEY
this.PACKAGE_NAME = process.env.INPUT_PACKAGE_NAME || process.env.PACKAGE_NAME
this.projectFile = process.env.INPUT_PROJECT_FILE_PATH
this.versionFile = process.env.INPUT_VERSION_FILE_PATH || process.env.VERSION_FILE_PATH
this.versionRegex = new RegExp(process.env.INPUT_VERSION_REGEX || process.env.VERSION_REGEX)
this.version = process.env.INPUT_VERSION_STATIC || process.env.VERSION_STATIC
this.tagCommit = JSON.parse(process.env.INPUT_TAG_COMMIT || process.env.TAG_COMMIT)
this.tagFormat = process.env.INPUT_TAG_FORMAT || process.env.TAG_FORMAT
this.nugetKey = process.env.INPUT_NUGET_KEY || process.env.NUGET_KEY
this.packageName = process.env.INPUT_PACKAGE_NAME || process.env.PACKAGE_NAME
}

_warn(msg) {
_printWarning(msg) {
console.log(`##[warning]${msg}`)
}

_fail(msg) {
_printErrorAndExit(msg) {
console.log(`##[error]${msg}`)
throw new Error(msg)
}

_execCmd(cmd, options) {
_executeCommand(cmd, options) {
const INPUT = cmd.split(" "), TOOL = INPUT[0], ARGS = INPUT.slice(1)
return spawnSync(TOOL, ARGS, options)
}

_execAndCapture(cmd) {
return this._execCmd(cmd, { encoding: "utf-8" }).stdout
_executeInProcess(cmd) {
this._executeCommand(cmd, { encoding: "utf-8", stdio: [process.stdin, process.stdout, process.stderr] })
}

_execInProc(cmd) {
this._execCmd(cmd, { encoding: "utf-8", stdio: [process.stdin, process.stdout, process.stderr] })
}
_mayResolveFilepath(filePath, errMsg) {
const fullPath = path.resolve(process.env.GITHUB_WORKSPACE, filePath)

if (!fs.existsSync(fullPath))
this._printErrorAndExit(errMsg)

_resolveIfExists(filePath, errMsg) {
const FULLPATH = path.resolve(process.env.GITHUB_WORKSPACE, filePath)
return fullPath
}

if (!fs.existsSync(FULLPATH))
this._fail(errMsg)
_tagCommit(version) {
const TAG = this.tagFormat.replace("*", version)

return FULLPATH
this._executeInProcess(`git tag ${TAG}`)
this._executeInProcess(`git push origin ${TAG}`)
}

_pushPackage() {
if (!this.NUGET_KEY) {
this._warn("😢 NUGET_KEY not given")
if (!this.nugetKey) {
this._printWarning("😢 NUGET_KEY not given")
return
}

if (!this._execAndCapture("dotnet --version")) {
this._warn("😭 dotnet not found")
return
}
fs.readdirSync(".").filter(fn => fn.endsWith("nupkg")).forEach(fn => fs.unlinkSync(fn))

this._execInProc(`dotnet build -c Release ${this.PROJECT_FILE_PATH}`)
this._execInProc(`dotnet pack --no-build -c Release ${this.PROJECT_FILE_PATH} -o .`)
this._executeInProcess(`dotnet build -c Release ${this.projectFile}`)
this._executeInProcess(`dotnet pack --include-symbols -p:SymbolPackageFormat=snupkg --no-build -c Release ${this.projectFile} -o .`)

const generatedPackage = fs.readdirSync(".").filter(fn => fn.startsWith(this.PACKAGE_NAME) && fn.endsWith(".nupkg")).shift()
console.log(`Generated Package: ${generatedPackage}`)
const packages = fs.readdirSync(".").filter(fn => fn.endsWith("nupkg"))
console.log(`Generated Package(s): ${packages.join(", ")}`)

const NUGET_PUSH_RESPONSE = this._execAndCapture(`dotnet nuget push ${generatedPackage} -s https://api.nuget.org/v3/index.json -k ${this.NUGET_KEY}`)
const NUGET_ERROR_REGEX = /(error: Response status code does not indicate success.*)/
const pushCmd = `dotnet nuget push *.nupkg -s https://api.nuget.org/v3/index.json -k ${this.nugetKey} --skip-duplicate`,
pushOutput = this._executeCommand(pushCmd, { encoding: "utf-8" }).stdout

console.log(NUGET_PUSH_RESPONSE)
console.log(pushOutput)

if (NUGET_ERROR_REGEX.test(NUGET_PUSH_RESPONSE))
this._fail(`😭 ${NUGET_ERROR_REGEX.exec(NUGET_PUSH_RESPONSE)[1]}`)
if (/error/.test(pushOutput))
this._printErrorAndExit(`😭 ${/error.*/.exec(pushOutput)[0]}`)
}

_tagCommit(version) {
if (this.TAG_COMMIT) {
const TAG = this.TAG_FORMAT.replace("*", version)

if (this._execAndCapture(`git ls-remote --tags origin ${TAG}`).indexOf(TAG) >= 0) {
this._warn(`😢 tag ${TAG} already exists`)
return
}
_mayTagAndPush(version, name) {
console.log(`👍 found a new version (${version}) of ${name}`)

this._execInProc(`git tag ${TAG}`)
this._execInProc(`git push origin ${TAG}`)
}
}
if (this.tagCommit)
this._tagCommit(version)

_pushAndTag(version, name) {
console.log(`👍 found a new version (${version}) of ${name}`)
this._tagCommit(version)
this._pushPackage()
}

run() {
if (!this.PROJECT_FILE_PATH)
this._fail("😭 project file not given")

this.PROJECT_FILE_PATH = this._resolveIfExists(this.PROJECT_FILE_PATH, "😭 project file not found")
_determineProjectFilepath() {
if (!this.projectFile)
this._printErrorAndExit("😭 project file not given")

console.log(`Project Filepath: ${this.PROJECT_FILE_PATH}`)

let CURRENT_VERSION

if (!this.VERSION_STATIC) {
this.VERSION_FILE_PATH = !this.VERSION_FILE_PATH ? this.PROJECT_FILE_PATH : this._resolveIfExists(this.VERSION_FILE_PATH, "😭 version file not found")
this.projectFile = this._mayResolveFilepath(this.projectFile, "😭 project file not found")
console.log(`Project Filepath: ${this.projectFile}`)
}

console.log(`Version Filepath: ${this.VERSION_FILE_PATH}`)
_determineVersion() {
if (!this.version) {
this.versionFile = !this.versionFile ? this.projectFile : this._mayResolveFilepath(this.versionFile, "😭 version file not found")
console.log(`Version Filepath: ${this.versionFile}`)

const FILE_CONTENT = fs.readFileSync(this.VERSION_FILE_PATH, { encoding: "utf-8" }),
VERSION_INFO = this.VERSION_REGEX.exec(FILE_CONTENT)
const versionFileContent = fs.readFileSync(this.versionFile, { encoding: "utf-8" }),
parsedVersion = this.versionRegex.exec(versionFileContent)

if (!VERSION_INFO)
this._fail("😢 unable to extract version info!")
if (!parsedVersion)
this._printErrorAndExit("😢 unable to extract version info!")

CURRENT_VERSION = VERSION_INFO[1]
} else
CURRENT_VERSION = this.VERSION_STATIC
this.version = parsedVersion[1]
}

console.log(`Version: ${CURRENT_VERSION}`)
console.log(`Version: ${this.version}`)
}

if (!this.PACKAGE_NAME) {
this.PACKAGE_NAME = path.basename(this.PROJECT_FILE_PATH).split(".").slice(0, -1).join(".")
console.log(`Package Name: ${this.PACKAGE_NAME}`)
_checkForUpdate() {
if (!this.packageName) {
this.packageName = path.basename(this.projectFile).split(".").slice(0, -1).join(".")
console.log(`Package Name: ${this.packageName}`)
}

https.get(`https://api.nuget.org/v3-flatcontainer/${this.PACKAGE_NAME}/index.json`, res => {
https.get(`https://api.nuget.org/v3-flatcontainer/${this.packageName}/index.json`, res => {
let body = ""

if (res.statusCode == 404)
this._pushAndTag(CURRENT_VERSION, this.PACKAGE_NAME)
this._mayTagAndPush(this.version, this.packageName)

if (res.statusCode == 200) {
res.setEncoding("utf8")
res.on("data", chunk => body += chunk)
res.on("end", () => {
const existingVersions = JSON.parse(body)
if (existingVersions.versions.indexOf(CURRENT_VERSION) < 0)
this._pushAndTag(CURRENT_VERSION, this.PACKAGE_NAME)
if (existingVersions.versions.indexOf(this.version) < 0)
this._mayTagAndPush(this.version, this.packageName)
})
}
}).on("error", e => {
this._warn(`😢 error reaching nuget.org ${e.message}`)
this._printWarning(`😢 error reaching nuget.org ${e.message}`)
})
}

run() {
this._determineProjectFilepath()
this._determineVersion()
this._checkForUpdate()
}
}

new Action().run()

0 comments on commit 0b59dc0

Please sign in to comment.