Skip to content
This repository was archived by the owner on Oct 26, 2022. It is now read-only.

Commit 23486ff

Browse files
author
Rohith Reddy
committed
add better support & control for versions & tags
Fixes #8 & #9
1 parent 9509064 commit 23486ff

File tree

3 files changed

+138
-81
lines changed

3 files changed

+138
-81
lines changed

README.md

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,52 +2,57 @@
22
GitHub action to build, pack & publish nuget packages automatically when a project version is updated
33

44
## Usage
5-
Create a new `.github/workflows/publish-nuget.yml` file:
5+
Create new `.github/workflows/publish.yml` file:
66

7-
```yaml
7+
```yml
88
name: publish to nuget
99
on:
1010
push:
1111
branches:
1212
- master # Your default release branch
1313
jobs:
1414
publish:
15-
name: publish to nuget
15+
name: list on nuget
1616
runs-on: ubuntu-latest
1717
steps:
18-
# Checkout
1918
- uses: actions/checkout@v2
2019

21-
# Optional step, add only for a specific dotnet version that doesn't come with ubuntu-latest / windows-latest
22-
# Visit bit.ly/2synnZl for a list of software that comes pre-installed with ubuntu-latest / windows-latest
20+
# Required for a specific dotnet version that doesn't come with ubuntu-latest / windows-latest
21+
# Visit bit.ly/2synnZl to see the list of SDKs that are pre-installed with ubuntu-latest / windows-latest
2322
# - name: Setup dotnet
2423
# uses: actions/setup-dotnet@v1
2524
# with:
2625
# dotnet-version: 3.1.100
2726

2827
# Publish
29-
- name: Publish if version is updated
30-
uses: rohith/publish-nuget@v1
31-
# with: # All inputs are optional (details given below)
32-
# project_dir: src # Defaults to repository root
33-
# tag_format: v* # [*] gets replaced with version
34-
# nuget_key: ${{secrets.NUGET_API_KEY}} # nuget.org API key
28+
- name: publish on version change
29+
uses: rohith/publish-nuget@v2
30+
with:
31+
PROJECT_FILE_PATH: Core/Core.csproj # Relative to repository root
32+
# VERSION_FILE_PATH: Directory.Build.props # Filepath with version info, relative to repository root. Defaults to project file
33+
# VERSION_REGEX: <Version>(.*)<\/Version> # Regex pattern to extract version info in a capturing group
34+
# TAG_COMMIT: true # Flag to enable / disalge git tagging
35+
# TAG_FORMAT: v* # Format of the git tag, [*] gets replaced with version
36+
# NUGET_KEY: ${{secrets.NUGET_API_KEY}} # nuget.org API key
3537
```
3638

37-
- Project version updates are monitored on every push / PR merge to master & a new tag is created to denote the updated version
38-
- If a `nuget_key` is present then the project gets built, packed & published to nuget.org
39+
- With all settings on default, updates to project version are monitored on every push / PR merge to master & a new tag is created
40+
- If a `NUGET_KEY` is present then the project gets built, packed & published to nuget.org
3941

4042
## Inputs
41-
All these inputs are optional
43+
Most of the inputs are optional
4244

43-
Input | Description
44-
--- | ---
45-
project_dir | Directory path containing the project file, defaults to repository root
46-
tag_format | Defaults to `v*` - `[*]` is a placeholder for the actual project version
47-
nuget_key | API key to authorize the package upload to nuget.org
45+
Input | Default Value | Description
46+
--- | --- | ---
47+
PROJECT_FILE_PATH | | File path of the project to be packaged, relative to repository root
48+
VERSION_FILE_PATH | `[PROJECT_FILE_PATH]` | File path containing version info, relative to repository root
49+
VERSION_REGEX | `<Version>(.*)<\/Version>` | Regex pattern to extract version info in a capturing group
50+
TAG_COMMIT | `true` | Flag to enable / disable git tagging
51+
TAG_FORMAT | `v*` | `[*]` is a placeholder for the actual project version
52+
NUGET_KEY | | API key to authorize the package upload to nuget.org
4853

4954
**Note:**
50-
`project_dir` & `tag_format` have default values but a package cannot be published without the `nuget_key`
55+
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)
5156

5257
## License
5358
[MIT](LICENSE)

action.yml

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,27 @@
11
name: Publish NuGet
22
author: Rohith Reddy (@rohith)
3-
description: Build, Pack & Publish NuGet packages with dotnet (.net core) on project version change
3+
description: Build, Pack & Publish NuGet packages with dotnet (.net) core on project version change
44

55
inputs:
6-
project_dir:
7-
description: Project (csproj) directory path relative from the root of repository
6+
PROJECT_FILE_PATH:
7+
description: Filepath of the project to be packaged, relative to root of repository
8+
required: true
9+
VERSION_FILE_PATH:
10+
description: Filepath containing version info, relative to root of repository. Defaults to `PROJECT_FILE_PATH` if not specified
811
required: false
9-
default: .
10-
tag_format:
11-
description: Format of the tag to be created, `*` is placeholder for actual version
12+
VERSION_REGEX:
13+
description: Regex (with version in a capturing group) to extract the version from `VERSION_FILE_PATH`
14+
required: false
15+
default: <Version>(.*)<\/Version>
16+
TAG_COMMIT:
17+
description: Whether to create a tag when there's a version change or not
18+
required: false
19+
default: true
20+
TAG_FORMAT:
21+
description: Format of the tag, `*` is the placeholder for actual version
1222
required: false
1323
default: v*
14-
nuget_key:
24+
NUGET_KEY:
1525
description: API key for the NuGet feed
1626
required: false
1727

index.js

Lines changed: 95 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,120 @@
1-
const resolve = require("path").resolve,
1+
const path = require("path"),
22
spawnSync = require("child_process").spawnSync,
3-
{ readdirSync, readFileSync } = require("fs")
3+
fs = require("fs"),
4+
https = require("https")
5+
6+
class Action {
7+
constructor() {
8+
this.PROJECT_FILE_PATH = process.env.INPUT_PROJECT_FILE_PATH
9+
this.VERSION_FILE_PATH = process.env.INPUT_VERSION_FILE_PATH || process.env.VERSION_FILE_PATH
10+
this.VERSION_REGEX = new RegExp(process.env.INPUT_VERSION_REGEX || process.env.VERSION_REGEX)
11+
this.TAG_COMMIT = JSON.parse(process.env.INPUT_TAG_COMMIT || process.env.TAG_COMMIT)
12+
this.TAG_FORMAT = process.env.INPUT_TAG_FORMAT || process.env.TAG_FORMAT
13+
this.NUGET_KEY = process.env.INPUT_NUGET_KEY || process.env.NUGET_KEY
14+
}
415

5-
function run() {
6-
// find project
7-
const repoDir = process.env.GITHUB_WORKSPACE,
8-
projDir = resolve(repoDir, process.env.INPUT_PROJECT_DIR),
9-
projFiles = readdirSync(projDir).filter(f => f.endsWith(".csproj") || f.endsWith(".fsproj"))
16+
_warn(msg) {
17+
console.log(`##[warning]${msg}`)
18+
}
1019

11-
if (projFiles.length === 0) {
12-
failed("😭 project not found")
13-
return
20+
_fail(msg) {
21+
console.log(`##[error]${msg}`)
22+
throw new Error(msg)
1423
}
1524

16-
// check for new version
17-
const projName = projFiles[0],
18-
projPath = resolve(projDir, projName),
19-
versionRegex = /<Version>(.*)<\/Version>/,
20-
projDetails = readFileSync(projPath, { encoding: "utf-8" }),
21-
isVersionPresent = versionRegex.test(projDetails)
25+
_execCmd(cmd, options) {
26+
const INPUT = cmd.split(" "), TOOL = INPUT[0], ARGS = INPUT.slice(1)
27+
return spawnSync(TOOL, ARGS, options)
28+
}
2229

23-
if (!isVersionPresent) {
24-
console.log(`##[warning]😢 skipping due to no version`)
25-
return
30+
_execAndCapture(cmd) {
31+
return this._execCmd(cmd, { encoding: "utf-8" }).stdout
2632
}
2733

28-
const currentVersion = versionRegex.exec(projDetails)[1],
29-
tagFormat = process.env.INPUT_TAG_FORMAT,
30-
tag = tagFormat.replace("*", currentVersion),
31-
istagPresent = runCap(`git ls-remote --tags origin ${tag}`).indexOf(tag) >= 0
34+
_execInProc(cmd) {
35+
this._execCmd(cmd, { encoding: "utf-8", stdio: [process.stdin, process.stdout, process.stderr] })
36+
}
3237

33-
if (istagPresent) {
34-
console.log(`##[warning]😢 tag ${currentVersion} already exists`)
35-
return
38+
_resolveIfExists(filePath, msg) {
39+
const FULLPATH = path.resolve(process.env.GITHUB_WORKSPACE, filePath)
40+
if (!fs.existsSync(FULLPATH)) this._fail(msg)
41+
return FULLPATH
3642
}
3743

38-
console.log(`👍 found a new version (${currentVersion}) of ${projName}`)
44+
_pushPackage() {
45+
if (!this.NUGET_KEY) {
46+
this._warn("😢 nuget_key not given")
47+
return
48+
}
3949

40-
runProc(`git tag ${tag}`)
41-
runProc(`git push origin ${tag}`)
50+
if (!this._execAndCapture("dotnet --version")) {
51+
this._warn("😭 dotnet not found")
52+
return
53+
}
4254

43-
// pack & push
44-
const nugetKey = process.env.INPUT_NUGET_KEY
55+
this._execInProc(`dotnet pack -c Release ${this.PROJECT_FILE_PATH} -o .`)
56+
const NUGET_PUSH_RESPONSE = this._execAndCapture(`dotnet nuget push *.nupkg -s https://api.nuget.org/v3/index.json -k ${this.NUGET_KEY}`)
57+
const NUGET_ERROR_REGEX = /(error: Response status code does not indicate success.*)/
4558

46-
if (!nugetKey) {
47-
console.log(`##[warning]😢 nuget_key not found`)
48-
return
59+
if (NUGET_ERROR_REGEX.test(NUGET_PUSH_RESPONSE))
60+
this._fail(`😭 ${NUGET_ERROR_REGEX.exec(NUGET_PUSH_RESPONSE)[1]}`)
4961
}
5062

51-
if (!runCap("dotnet --version")) {
52-
failed("😭 dotnet not found")
53-
return
63+
_tagCommit(version) {
64+
if (this.TAG_COMMIT) {
65+
const TAG = this.TAG_FORMAT.replace("*", version)
66+
67+
if (this._execAndCapture(`git ls-remote --tags origin ${TAG}`).indexOf(TAG) >= 0) {
68+
this._warn(`😢 tag ${TAG} already exists`)
69+
return
70+
}
71+
72+
this._execInProc(`git tag ${TAG}`)
73+
this._execInProc(`git push origin ${TAG}`)
74+
}
5475
}
5576

56-
runProc(`dotnet pack -c Release ${projPath} -o .`)
57-
const out = runCap(`dotnet nuget push *.nupkg -s https://api.nuget.org/v3/index.json -k ${nugetKey}`)
58-
const errorRegex = /(error: Response status code does not indicate success.*)/
77+
_pushAndTag(CURRENT_VERSION, PACKAGE_NAME) {
78+
console.log(`👍 found a new version (${CURRENT_VERSION}) of ${PACKAGE_NAME}`)
79+
this._tagCommit(CURRENT_VERSION)
80+
this._pushPackage()
81+
}
5982

60-
if (errorRegex.test(out))
61-
failed(`😭 ${errorRegex.exec(out)[1]}`)
62-
}
83+
run() {
84+
if (!this.PROJECT_FILE_PATH)
85+
this._fail("😭 project file not given")
6386

64-
function runCap(cmd) { return runCmd(cmd, { encoding: "utf-8" }).stdout }
87+
this.PROJECT_FILE_PATH = this._resolveIfExists(this.PROJECT_FILE_PATH, "😭 project file not found")
88+
this.VERSION_FILE_PATH = !this.VERSION_FILE_PATH ? this.PROJECT_FILE_PATH : this._resolveIfExists(this.VERSION_FILE_PATH, "😭 version file not found")
6589

66-
function runProc(cmd) { runCmd(cmd, { encoding: "utf-8", stdio: [process.stdin, process.stdout, process.stderr] }) }
90+
const FILE_CONTENT = fs.readFileSync(this.VERSION_FILE_PATH, { encoding: "utf-8" }),
91+
VERSION_INFO = this.VERSION_REGEX.exec(FILE_CONTENT)
6792

68-
function runCmd(cmd, options) {
69-
const input = cmd.split(" "), tool = input[0], args = input.slice(1)
70-
return spawnSync(tool, args, options)
71-
}
93+
if (!VERSION_INFO)
94+
this._fail("😢 unable to extract version info")
95+
96+
const CURRENT_VERSION = VERSION_INFO[1],
97+
PACKAGE_NAME = path.basename(this.PROJECT_FILE_PATH).split(".").slice(0, -1).join(".")
7298

73-
function failed(msg) {
74-
process.exitCode = 1
75-
console.log(`##[error]${msg}`)
99+
https.get(`https://api.nuget.org/v3-flatcontainer/${PACKAGE_NAME}/index.json`, res => {
100+
let body = ""
101+
102+
if (res.statusCode == 404)
103+
this._pushAndTag(CURRENT_VERSION, PACKAGE_NAME)
104+
105+
if (res.statusCode == 200) {
106+
res.setEncoding("utf8")
107+
res.on("data", chunk => body += chunk)
108+
res.on("end", () => {
109+
const existingVersions = JSON.parse(body)
110+
if (existingVersions.versions.indexOf(CURRENT_VERSION) < 0)
111+
this._pushAndTag(CURRENT_VERSION, PACKAGE_NAME)
112+
})
113+
}
114+
}).on("error", e => {
115+
this._warn(`😢 error reaching nuget.org ${e.message}`)
116+
})
117+
}
76118
}
77119

78-
run()
120+
new Action().run()

0 commit comments

Comments
 (0)