diff --git a/README.md b/README.md index 2f76431..2bbe136 100644 --- a/README.md +++ b/README.md @@ -12,49 +12,91 @@ on: - master # Default release branch jobs: publish: - name: build, pack & push + name: build, pack & publish runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - # 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 + # dotnet-version: 3.1.200 # Publish - name: publish on version change + id: publish_nuget uses: rohith/publish-nuget@v2 with: - PROJECT_FILE_PATH: Core/Core.csproj # Relative to repository root - # 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> # Regex pattern to extract version info in a capturing group - # 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}} # API key for the NuGet feed + # Filepath of the project to be packaged, relative to root of repository + PROJECT_FILE_PATH: Core/Core.csproj + + # NuGet package id, used for version detection & defaults to project name + # PACKAGE_NAME: Core + + # Filepath with version info, relative to root of repository & defaults to PROJECT_FILE_PATH + # VERSION_FILE_PATH: Directory.Build.props + + # Regex pattern to extract version info in a capturing group + # VERSION_REGEX: (.*)<\/Version> + + # Useful with external providers like Nerdbank.GitVersioning, ignores VERSION_FILE_PATH & VERSION_REGEX + # VERSION_STATIC: 1.0.0 + + # Flag to toggle git tagging, enabled by default + # TAG_COMMIT: true + + # Format of the git tag, [*] gets replaced with actual version + # TAG_FORMAT: v* + + # API key to authenticate with NuGet server + # NUGET_KEY: ${{secrets.NUGET_API_KEY}} + + # API key to authenticate with NuGet symbols server, defaults to NUGET_KEY + # NUGET_SYMBOL_KEY: ${{secrets.NUGET_SYMBOLS_API_KEY}} + + # NuGet server uri hosting the packages, defaults to https://api.nuget.org + # NUGET_SOURCE: https://api.nuget.org + + # NuGet server uri hosting the symbols, defaults to https://api.nuget.org + # NUGET_SYMBOL_SOURCE: https://api.nuget.org + + # Flag to toggle pushing symbols along with nuget package to the server, enabled by default + # INCLUDE_SYMBOLS: true ``` -- Project gets built, packed & published only if there's a `NUGET_KEY` configured in the repository +- Project gets published only if there's a `NUGET_KEY` configured in the repository ## Inputs Input | Default Value | Description --- | --- | --- 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 +PACKAGE_NAME | | NuGet package id, used for version detection & defaults to project name +VERSION_FILE_PATH | `[PROJECT_FILE_PATH]` | Filepath with version info, relative to root of repository & defaults to PROJECT_FILE_PATH VERSION_REGEX | `(.*)<\/Version>` | Regex pattern to extract version info in a capturing group -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 | | API key for the NuGet feed +VERSION_STATIC| | Useful with external providers like Nerdbank.GitVersioning, ignores VERSION_FILE_PATH & VERSION_REGEX +TAG_COMMIT | `true` | Flag to toggle git tagging, enabled by default +TAG_FORMAT | `v*` | Format of the git tag, `[*]` gets replaced with actual version +NUGET_KEY | | API key to authenticate with NuGet server +NUGET_SYMBOL_KEY | `[NUGET_KEY]` | API key to authenticate with NuGet symbols server, defaults to NUGET_KEY +NUGET_SOURCE | `https://api.nuget.org` | NuGet server uri hosting the packages, defaults to https://api.nuget.org +NUGET_SYMBOL_SOURCE | `https://api.nuget.org` | NuGet server uri hosting the symbols, defaults to https://api.nuget.org +INCLUDE_SYMBOLS | `true` | Flag to toggle pushing symbols along with nuget package to the server, enabled by default + +## Outputs + +Output | Description +--- | --- +VERSION | Version of the associated git tag +PACKAGE_NAME | Name of the NuGet package generated +PACKAGE_PATH | Path to the generated NuGet package +SYMBOLS_PACKAGE_NAME | Name of the symbols package generated +SYMBOLS_PACKAGE_PATH | Path to the generated symbols package -**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) +**FYI:** +- Outputs may not be set if the action failed +- `NUGET_SOURCE` must support `/v3-flatcontainer/PACKAGE_NAME/index.json` for version change detection to work +- 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) diff --git a/action.yml b/action.yml index 8883331..56ca29b 100644 --- a/action.yml +++ b/action.yml @@ -7,29 +7,60 @@ inputs: 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 + description: NuGet package id, used for version detection & defaults to project name required: false VERSION_FILE_PATH: - description: Filepath containing version info, relative to root of repository + description: Filepath with version info, relative to root of repository & defaults to PROJECT_FILE_PATH required: false VERSION_REGEX: description: Regex pattern to extract version info in a capturing group required: false default: (.*)<\/Version> VERSION_STATIC: - description: Static version, useful for external providers like Nerdbank.GitVersioning + description: Useful with external providers like Nerdbank.GitVersioning, ignores VERSION_FILE_PATH & VERSION_REGEX required: false TAG_COMMIT: - description: Flag to enable / disable git tagging + description: Flag to toggle git tagging, enabled by default required: false default: true TAG_FORMAT: - description: Format of the git tag, `[*]` gets replaced with version + description: Format of the git tag, [*] gets replaced with actual version required: false default: v* NUGET_KEY: - description: API key for the NuGet feed + description: API key to authenticate with NuGet server required: false + NUGET_SYMBOL_KEY: + description: API key to authenticate with NuGet symbols server, defaults to NUGET_KEY + required: false + NUGET_SOURCE: + description: NuGet server uri hosting the packages, defaults to https://api.nuget.org + required: false + default: https://api.nuget.org + NUGET_SYMBOL_SOURCE: + description: NuGet server uri hosting the symbols, defaults to https://api.nuget.org + required: false + default: https://api.nuget.org + INCLUDE_SYMBOLS: + description: Flag to toggle pushing symbols along with nuget package to the server, enabled by default + required: false + default: true + +outputs: + VERSION: + description: Version of the associated git tag + + PACKAGE_NAME: + description: Name of the NuGet package generated + + PACKAGE_PATH: + description: Path to the generated NuGet package + + SYMBOLS_PACKAGE_NAME: + description: Name of the symbols package generated + + SYMBOLS_PACKAGE_PATH: + description: Path to the generated symbols package runs: using: node12 diff --git a/index.js b/index.js index 3016045..2c0e81b 100644 --- a/index.js +++ b/index.js @@ -1,30 +1,33 @@ -const path = require("path"), - spawnSync = require("child_process").spawnSync, +const os = require("os"), fs = require("fs"), - https = require("https") + path = require("path"), + https = require("https"), + spawnSync = require("child_process").spawnSync class Action { constructor() { this.projectFile = process.env.INPUT_PROJECT_FILE_PATH - this.versionFile = process.env.INPUT_VERSION_FILE_PATH || process.env.VERSION_FILE_PATH + this.packageName = process.env.INPUT_PACKAGE_NAME || process.env.PACKAGE_NAME + this.versionFile = process.env.INPUT_VERSION_FILE_PATH || process.env.VERSION_FILE_PATH || this.projectFile 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 - } - - _printWarning(msg) { - console.log(`##[warning]${msg}`) + this.nugetSymbolKey = process.env.INPUT_NUGET_SYMBOL_KEY || process.env.NUGET_SYMBOL_KEY || this.nugetKey + this.nugetSource = process.env.INPUT_NUGET_SOURCE || process.env.NUGET_SOURCE + this.nugetSymbolSource = process.env.INPUT_NUGET_SYMBOL_SOURCE || process.env.NUGET_SYMBOL_SOURCE + this.includeSymbols = JSON.parse(process.env.INPUT_INCLUDE_SYMBOLS || process.env.INCLUDE_SYMBOLS) } _printErrorAndExit(msg) { - console.log(`##[error]${msg}`) + console.log(`##[error]😭 ${msg}`) throw new Error(msg) } _executeCommand(cmd, options) { + console.log(`executing: [${cmd}]`) + const INPUT = cmd.split(" "), TOOL = INPUT[0], ARGS = INPUT.slice(1) return spawnSync(TOOL, ARGS, options) } @@ -33,90 +36,74 @@ class Action { this._executeCommand(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) - - return fullPath - } - _tagCommit(version) { const TAG = this.tagFormat.replace("*", version) + console.log(`✨ creating new tag ${TAG}`) + this._executeInProcess(`git tag ${TAG}`) this._executeInProcess(`git push origin ${TAG}`) + + process.stdout.write(`::set-output name=VERSION::${TAG}` + os.EOL) } - _pushPackage() { + _pushPackage(version, name) { + console.log(`✨ found new version (${version}) of ${name}`) + if (!this.nugetKey) { - this._printWarning("😢 NUGET_KEY not given") + console.log("##[warning]😢 NUGET_KEY not given") return } + console.log(`NuGet Source: ${this.nugetSource}`) + console.log(`NuGet Symbol Source: ${this.nugetSymbolSource}`) + fs.readdirSync(".").filter(fn => fn.endsWith("nupkg")).forEach(fn => fs.unlinkSync(fn)) 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 packSymbolFlags = this.includeSymbols ? "--include-symbols -p:SymbolPackageFormat=snupkg" : "" + this._executeInProcess(`dotnet pack ${packSymbolFlags} --no-build -c Release ${this.projectFile} -o .`) const packages = fs.readdirSync(".").filter(fn => fn.endsWith("nupkg")) console.log(`Generated Package(s): ${packages.join(", ")}`) - const pushCmd = `dotnet nuget push *.nupkg -s https://api.nuget.org/v3/index.json -k ${this.nugetKey} --skip-duplicate`, + const pushSymbolFlags = this.includeSymbols ? `-ss ${this.nugetSymbolSource}/v3/index.json -sk ${this.nugetSymbolKey}` : "--no-symbols", + pushCmd = `dotnet nuget push *.nupkg -s ${this.nugetSource}/v3/index.json -k ${this.nugetKey} ${pushSymbolFlags} --skip-duplicate`, pushOutput = this._executeCommand(pushCmd, { encoding: "utf-8" }).stdout console.log(pushOutput) if (/error/.test(pushOutput)) - this._printErrorAndExit(`😭 ${/error.*/.exec(pushOutput)[0]}`) - } + this._printErrorAndExit(`${/error.*/.exec(pushOutput)[0]}`) - _mayTagAndPush(version, name) { - console.log(`👍 found a new version (${version}) of ${name}`) + const packageFilename = packages.filter(p => p.endsWith(".nupkg"))[0], + symbolsFilename = packages.filter(p => p.endsWith(".snupkg"))[0] - if (this.tagCommit) - this._tagCommit(version) + process.stdout.write(`::set-output name=PACKAGE_NAME::${packageFilename}` + os.EOL) + process.stdout.write(`::set-output name=PACKAGE_PATH::${path.resolve(packageFilename)}` + os.EOL) - this._pushPackage() - } - - _determineProjectFilepath() { - if (!this.projectFile) - this._printErrorAndExit("😭 project file not given") - - this.projectFile = this._mayResolveFilepath(this.projectFile, "😭 project file not found") - console.log(`Project Filepath: ${this.projectFile}`) - } - - _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 versionFileContent = fs.readFileSync(this.versionFile, { encoding: "utf-8" }), - parsedVersion = this.versionRegex.exec(versionFileContent) - - if (!parsedVersion) - this._printErrorAndExit("😢 unable to extract version info!") - - this.version = parsedVersion[1] + if (symbolsFilename) { + process.stdout.write(`::set-output name=SYMBOLS_PACKAGE_NAME::${symbolsFilename}` + os.EOL) + process.stdout.write(`::set-output name=SYMBOLS_PACKAGE_PATH::${path.resolve(symbolsFilename)}` + os.EOL) } - console.log(`Version: ${this.version}`) + if (this.tagCommit) + this._tagCommit(version) } _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.packageName}/index.json`, res => { + console.log(`Package Name: ${this.packageName}`) + + https.get(`${this.nugetSource}/v3-flatcontainer/${this.packageName}/index.json`, res => { let body = "" if (res.statusCode == 404) - this._mayTagAndPush(this.version, this.packageName) + this._pushPackage(this.version, this.packageName) if (res.statusCode == 200) { res.setEncoding("utf8") @@ -124,17 +111,38 @@ class Action { res.on("end", () => { const existingVersions = JSON.parse(body) if (existingVersions.versions.indexOf(this.version) < 0) - this._mayTagAndPush(this.version, this.packageName) + this._pushPackage(this.version, this.packageName) }) } }).on("error", e => { - this._printWarning(`😢 error reaching nuget.org ${e.message}`) + this._printErrorAndExit(`error: ${e.message}`) }) } run() { - this._determineProjectFilepath() - this._determineVersion() + if (!this.projectFile || !fs.existsSync(this.projectFile)) + this._printErrorAndExit("project file not found") + + console.log(`Project Filepath: ${this.projectFile}`) + + if (!this.version) { + if (this.versionFile !== this.projectFile && !fs.existsSync(this.versionFile)) + this._printErrorAndExit("version file not found") + + console.log(`Version Filepath: ${this.versionFile}`) + console.log(`Version Regex: ${this.versionRegex}`) + + const versionFileContent = fs.readFileSync(this.versionFile, { encoding: "utf-8" }), + parsedVersion = this.versionRegex.exec(versionFileContent) + + if (!parsedVersion) + this._printErrorAndExit("unable to extract version info!") + + this.version = parsedVersion[1] + } + + console.log(`Version: ${this.version}`) + this._checkForUpdate() } }