Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| af46017d0a | |||
| 68d642a9f6 | |||
| f4e67395fe | |||
| 963a767a85 | |||
| 69cf2d493b | |||
| 38ec82c700 | |||
| 4c32e563be | |||
| e2d3fdbd16 | |||
| 9841c96e72 | |||
| 9c882b3092 | |||
| 9ce866a51b | |||
| 1adbfc3eda | |||
| 16abb1ee5b | |||
| acdeb65277 | |||
| 3ed460c651 | |||
| aea67fe6c1 | |||
| 924a4da489 | |||
| 9f97eda4df | |||
| b344a86b03 | |||
| 2c0369c005 | |||
| 93fc0fde71 | |||
| 39d1f32564 | |||
| 858b300bdb | |||
| 6be68d57de | |||
| 3ac70b77f8 | |||
| 1437a4d686 | |||
| 04acb22164 | |||
| 4d4f066600 | |||
| 094c4c7b0f | |||
| d3f0376d67 | |||
| 0e6aab82a5 | |||
| 5b2f1fe9d4 | |||
| a6e0311a4b | |||
| 37b7b07e3f | |||
| d8bc68ab96 | |||
| 3ee34e6241 | |||
| 475ca7533e | |||
| 839f25d597 | |||
| 5b9105a42d |
@@ -4,16 +4,23 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "action.yml"
|
||||
- "src/**"
|
||||
- "dist/**"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
- run: npm ci
|
||||
|
||||
@@ -8,8 +8,8 @@ jobs:
|
||||
name: Test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
- run: npm ci
|
||||
@@ -24,8 +24,8 @@ jobs:
|
||||
name: Check Dist
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
- run: npm ci
|
||||
@@ -43,10 +43,10 @@ jobs:
|
||||
name: Dry-Run
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
- run: npm ci
|
||||
@@ -63,3 +63,37 @@ jobs:
|
||||
fi
|
||||
|
||||
echo "New tag: ${{ steps.increment-tag.outputs.new-tag }}"
|
||||
|
||||
dry-run-prerelease:
|
||||
name: Dry-Run Prerelease
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
- run: npm ci
|
||||
- name: Create prerelease tag
|
||||
id: prerelease-tag
|
||||
uses: ./
|
||||
with:
|
||||
token: ${{ secrets.GITEA_TOKEN }}
|
||||
prerelease: "true"
|
||||
- name: Print new tag
|
||||
run: |
|
||||
if [ -z "${{ steps.prerelease-tag.outputs.new-tag }}" ]; then
|
||||
echo "No new tag found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "New tag: ${{ steps.prerelease-tag.outputs.new-tag }}"
|
||||
- name: Verify prerelease tag format
|
||||
run: |
|
||||
if [[ "${{ steps.prerelease-tag.outputs.new-tag }}" =~ -rc-[a-f0-9]{7,}$ ]]; then
|
||||
echo "✓ Prerelease tag format is correct"
|
||||
else
|
||||
echo "✗ Prerelease tag format is incorrect. Expected format: X.Y.Z-rc-<sha>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
20
README.md
20
README.md
@@ -43,6 +43,15 @@ jobs:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
```
|
||||
|
||||
### With prerelease tags
|
||||
|
||||
```yaml
|
||||
- uses: tbehrendt/conventional-semantic-git-tag-increment@v1
|
||||
with:
|
||||
prerelease: "true"
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
| Commit Message | Current Tag | New Tag | Reason |
|
||||
@@ -52,11 +61,20 @@ jobs:
|
||||
| `feat!: change API response format` | `v1.1.1` | `v2.0.0` | Breaking change |
|
||||
| `docs: update README` | `v2.0.0` | `v2.0.1` | Documentation update |
|
||||
|
||||
### Prerelease Examples
|
||||
|
||||
| Commit Message | Current Tag | New Tag (prerelease) | Reason |
|
||||
| ------------------------------- | ----------- | -------------------- | ----------- |
|
||||
| `feat: add user authentication` | `v1.0.0` | `1.1.0-rc-abc123` | New feature |
|
||||
| `fix: resolve login bug` | `v1.1.0` | `1.1.1-rc-def456` | Bug fix |
|
||||
|
||||
## Inputs
|
||||
|
||||
- `last-tag` (optional): Starting tag to increment from. If not provided, uses the latest tag in the repository.
|
||||
- `token` (required): GitHub token for repository access. Use `${{ github.token }}` for public repos or a PAT for private repos.
|
||||
- `prerelease` (optional): Whether to create a prerelease tag with `-rc-<github.sha>` suffix. Defaults to `false`.
|
||||
- `max-tags` (optional): Maximum number of tags to fetch when looking for the latest non-pre-release tag. Defaults to `50`.
|
||||
|
||||
## Outputs
|
||||
|
||||
- `new-tag`: The incremented semantic version tag (e.g., `1.2.3`)
|
||||
- `new-tag`: The incremented semantic version tag (e.g., `1.2.3` or `1.2.3-rc-abc123` for prerelease)
|
||||
|
||||
10
action.yml
10
action.yml
@@ -11,11 +11,19 @@ inputs:
|
||||
description: "Token for repository access"
|
||||
required: true
|
||||
default: "${{ github.token }}"
|
||||
prerelease:
|
||||
description: "Whether to create a prerelease tag"
|
||||
required: false
|
||||
default: "false"
|
||||
max-tags:
|
||||
description: "Maximum number of tags to fetch when looking for the latest non-pre-release tag"
|
||||
required: false
|
||||
default: "50"
|
||||
|
||||
outputs:
|
||||
new-tag:
|
||||
description: "The new incremented git tag"
|
||||
|
||||
runs:
|
||||
using: "node20"
|
||||
using: "node24"
|
||||
main: "dist/index.js"
|
||||
|
||||
132
dist/index.js
vendored
132
dist/index.js
vendored
File diff suppressed because one or more lines are too long
@@ -1,12 +0,0 @@
|
||||
import { build } from "esbuild";
|
||||
|
||||
await build({
|
||||
entryPoints: ["src/index.ts"],
|
||||
bundle: true,
|
||||
platform: "node",
|
||||
target: "node20",
|
||||
outfile: "dist/index.js",
|
||||
sourcemap: false,
|
||||
minify: true,
|
||||
logLevel: "info",
|
||||
});
|
||||
1162
package-lock.json
generated
1162
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
22
package.json
22
package.json
@@ -4,24 +4,24 @@
|
||||
"main": "src/index.ts",
|
||||
"scripts": {
|
||||
"typecheck": "tsc --noEmit",
|
||||
"build": "esbuild src/index.ts --bundle --outfile=dist/index.js --platform=node --target=node20 --minify",
|
||||
"build": "esbuild src/index.ts --bundle --outfile=dist/index.js --platform=node --target=node24 --minify",
|
||||
"test": "jest",
|
||||
"format": "prettier --write src/**/*.ts"
|
||||
},
|
||||
"author": "Timo Behrendt <t.behrendt@t00n.de>",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/conventional-commits-parser": "^5.0.1",
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/node": "^24.3.0",
|
||||
"esbuild": "^0.25.9",
|
||||
"jest": "^30.0.5",
|
||||
"prettier": "^3.6.2",
|
||||
"ts-jest": "^29.4.1",
|
||||
"typescript": "^5.9.2"
|
||||
"@types/conventional-commits-parser": "5.0.2",
|
||||
"@types/jest": "30.0.0",
|
||||
"@types/node": "24.10.10",
|
||||
"esbuild": "0.27.2",
|
||||
"jest": "30.2.0",
|
||||
"prettier": "3.8.1",
|
||||
"ts-jest": "29.4.6",
|
||||
"typescript": "5.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.11.1",
|
||||
"conventional-commits-parser": "^3.2.4"
|
||||
"@actions/core": "2.0.3",
|
||||
"conventional-commits-parser": "3.2.4"
|
||||
}
|
||||
}
|
||||
|
||||
18
renovate.json
Normal file
18
renovate.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"local>t.behrendt/renovate-configs:common",
|
||||
"local>t.behrendt/renovate-configs:action"
|
||||
],
|
||||
"postUpgradeTasks": {
|
||||
"commands": ["npm ci", "npm run build"],
|
||||
"fileFilters": ["dist/**"],
|
||||
"executionMode": "update"
|
||||
},
|
||||
"packageRules": [
|
||||
{
|
||||
"matchUpdateTypes": ["patch", "minor"],
|
||||
"groupName": "dependencies (non-major)"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -149,6 +149,16 @@ describe("TagService", () => {
|
||||
const result = tagService.renderTag(tag, "v");
|
||||
expect(result).toBe("v1.2.3");
|
||||
});
|
||||
|
||||
it("should render tag with prerelease suffix", () => {
|
||||
const result = tagService.renderTag(tag, "", "-rc-abc123");
|
||||
expect(result).toBe("1.2.3-rc-abc123");
|
||||
});
|
||||
|
||||
it("should render tag with prefix and prerelease suffix", () => {
|
||||
const result = tagService.renderTag(tag, "v", "-rc-abc123");
|
||||
expect(result).toBe("v1.2.3-rc-abc123");
|
||||
});
|
||||
});
|
||||
|
||||
describe("parseTag", () => {
|
||||
@@ -221,7 +231,7 @@ describe("TagIncrementer", () => {
|
||||
const result = await tagIncrementer.getBaseTag(undefined);
|
||||
|
||||
expect(result).toBe("v2.0.0");
|
||||
expect(mockCoreService.getLogs()).toContain("INFO: Using latest tag: v2.0.0");
|
||||
expect(mockCoreService.getLogs()).toContain("INFO: Using latest real tag: v2.0.0");
|
||||
});
|
||||
|
||||
it("should default to 0.0.0 when no tags found", async () => {
|
||||
@@ -234,6 +244,34 @@ describe("TagIncrementer", () => {
|
||||
"WARNING: No tags found in repository, defaulting to 0.0.0"
|
||||
);
|
||||
});
|
||||
|
||||
it("should skip pre-release tags and use latest real tag", async () => {
|
||||
mockGitService.setMockTags([
|
||||
{ name: "v2.0.0-rc-26035c6e13" },
|
||||
{ name: "v1.2.3" },
|
||||
{ name: "v1.1.4-rc-aa512edb16" },
|
||||
{ name: "v1.0.0" },
|
||||
]);
|
||||
|
||||
const result = await tagIncrementer.getBaseTag(undefined);
|
||||
|
||||
expect(result).toBe("v1.2.3");
|
||||
expect(mockCoreService.getLogs()).toContain("INFO: Using latest real tag: v1.2.3");
|
||||
});
|
||||
|
||||
it("should default to 0.0.0 when only pre-release tags found", async () => {
|
||||
mockGitService.setMockTags([
|
||||
{ name: "v2.0.0-rc-26035c6e13" },
|
||||
{ name: "v1.1.4-rc-aa512edb16" },
|
||||
]);
|
||||
|
||||
const result = await tagIncrementer.getBaseTag(undefined);
|
||||
|
||||
expect(result).toBe("0.0.0");
|
||||
expect(mockCoreService.getWarnings()).toContain(
|
||||
"WARNING: No real tags found (only pre-release tags), defaulting to 0.0.0"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("determineIncrementType", () => {
|
||||
@@ -302,6 +340,28 @@ describe("TagIncrementer", () => {
|
||||
|
||||
expect(result).toBe("2.1.1");
|
||||
});
|
||||
|
||||
it("should create prerelease tag when prerelease is true", async () => {
|
||||
mockGitService.setMockTags([{ name: "v1.2.3" }]);
|
||||
mockGitService.setMockCommit({
|
||||
commit: { message: "feat: new feature" },
|
||||
});
|
||||
|
||||
const result = await tagIncrementer.incrementTag(undefined, "ref", 50, true, "abc123");
|
||||
|
||||
expect(result).toBe("1.3.0-rc-abc123");
|
||||
});
|
||||
|
||||
it("should not create prerelease tag when prerelease is false", async () => {
|
||||
mockGitService.setMockTags([{ name: "v1.2.3" }]);
|
||||
mockGitService.setMockCommit({
|
||||
commit: { message: "feat: new feature" },
|
||||
});
|
||||
|
||||
const result = await tagIncrementer.incrementTag(undefined, "ref", 50, false, "abc123");
|
||||
|
||||
expect(result).toBe("1.3.0");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -315,14 +375,18 @@ describe("LocalGitService", () => {
|
||||
|
||||
describe("listTags", () => {
|
||||
it("should call git command to list tags", async () => {
|
||||
mockExecSync.mockReturnValue("v1.0.0\nv1.1.0\n" as any);
|
||||
mockExecSync.mockReturnValue("v1.0.0\n1.1.0\n2.0.0-rc-26035c6e13\n" as any);
|
||||
|
||||
const result = await localGitService.listTags(5);
|
||||
|
||||
expect(mockExecSync).toHaveBeenCalledWith("git tag --sort=-version:refname", {
|
||||
encoding: "utf8",
|
||||
});
|
||||
expect(result).toEqual([{ name: "v1.0.0" }, { name: "v1.1.0" }]);
|
||||
expect(result).toEqual([
|
||||
{ name: "v1.0.0" },
|
||||
{ name: "1.1.0" },
|
||||
{ name: "2.0.0-rc-26035c6e13" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("should return empty array when no tags exist", async () => {
|
||||
@@ -391,9 +455,12 @@ describe("LocalCoreService", () => {
|
||||
// Integration test example
|
||||
describe("TagIncrementer Integration", () => {
|
||||
it("should handle complete tag increment flow", async () => {
|
||||
const mockGitService = new MockGitService([{ name: "v1.2.3" }], {
|
||||
commit: { message: "feat: new feature" },
|
||||
});
|
||||
const mockGitService = new MockGitService(
|
||||
[{ name: "v2.0.0-rc-26035c6e13" }, { name: "v1.1.4-rc-aa512edb16" }, { name: "v1.2.3" }],
|
||||
{
|
||||
commit: { message: "feat: new feature" },
|
||||
}
|
||||
);
|
||||
const mockCoreService = new MockCoreService();
|
||||
const commitAnalyzer = new ConventionalCommitAnalyzer();
|
||||
const tagService = new TagService();
|
||||
@@ -410,5 +477,23 @@ describe("TagIncrementer Integration", () => {
|
||||
expect(result).toBe("1.3.0");
|
||||
expect(mockCoreService.getLogs()).toContain("INFO: Determined increment type: minor");
|
||||
expect(mockCoreService.getLogs()).toContain("INFO: New tag: 1.3.0");
|
||||
expect(mockCoreService.getLogs()).toContain("INFO: Using latest real tag: v1.2.3");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Prerelease Validation", () => {
|
||||
it("should validate prerelease configuration correctly", () => {
|
||||
// Test cases for prerelease validation logic
|
||||
const testCases = [
|
||||
{ prerelease: true, githubSha: "abc123", shouldFail: false },
|
||||
{ prerelease: true, githubSha: "", shouldFail: true },
|
||||
{ prerelease: false, githubSha: "abc123", shouldFail: false },
|
||||
{ prerelease: false, githubSha: "", shouldFail: false },
|
||||
];
|
||||
|
||||
testCases.forEach(({ prerelease, githubSha, shouldFail }) => {
|
||||
const result = prerelease && !githubSha;
|
||||
expect(result).toBe(shouldFail);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
58
src/main.ts
58
src/main.ts
@@ -73,8 +73,8 @@ export class TagService {
|
||||
}
|
||||
}
|
||||
|
||||
renderTag(tag: Tag, prefix: string = ""): string {
|
||||
return `${prefix}${tag.major}.${tag.minor}.${tag.patch}`;
|
||||
renderTag(tag: Tag, prefix: string = "", prereleaseSuffix: string = ""): string {
|
||||
return `${prefix}${tag.major}.${tag.minor}.${tag.patch}${prereleaseSuffix}`;
|
||||
}
|
||||
|
||||
parseTag(tagString: string): Tag {
|
||||
@@ -103,22 +103,29 @@ export class TagIncrementer {
|
||||
private tagService: TagService
|
||||
) {}
|
||||
|
||||
async getBaseTag(lastTag: string | undefined): Promise<string> {
|
||||
async getBaseTag(lastTag: string | undefined, maxTags: number = 50): Promise<string> {
|
||||
if (lastTag) {
|
||||
this.coreService.info(`Using provided tag: ${lastTag}`);
|
||||
return lastTag;
|
||||
}
|
||||
|
||||
this.coreService.info("Fetching tags from local repository");
|
||||
const tags = await this.gitService.listTags(1);
|
||||
this.coreService.info(`Fetching up to ${maxTags} tags from local repository`);
|
||||
const tags = await this.gitService.listTags(maxTags);
|
||||
|
||||
if (tags.length === 0) {
|
||||
this.coreService.warning("No tags found in repository, defaulting to 0.0.0");
|
||||
return "0.0.0";
|
||||
}
|
||||
|
||||
const baseTag = tags[0].name;
|
||||
this.coreService.info(`Using latest tag: ${baseTag}`);
|
||||
const realTags = tags.filter((tag) => !tag.name.includes("-rc-"));
|
||||
|
||||
if (realTags.length === 0) {
|
||||
this.coreService.warning("No real tags found (only pre-release tags), defaulting to 0.0.0");
|
||||
return "0.0.0";
|
||||
}
|
||||
|
||||
const baseTag = realTags[0].name;
|
||||
this.coreService.info(`Using latest real tag: ${baseTag}`);
|
||||
return baseTag;
|
||||
}
|
||||
|
||||
@@ -155,10 +162,16 @@ export class TagIncrementer {
|
||||
}
|
||||
}
|
||||
|
||||
async incrementTag(lastTag: string | undefined, ref: string): Promise<string> {
|
||||
async incrementTag(
|
||||
lastTag: string | undefined,
|
||||
ref: string,
|
||||
maxTags: number = 50,
|
||||
prerelease: boolean = false,
|
||||
githubSha: string = ""
|
||||
): Promise<string> {
|
||||
this.coreService.info(`Starting tag increment process at ref ${ref}`);
|
||||
|
||||
const baseTag = await this.getBaseTag(lastTag);
|
||||
const baseTag = await this.getBaseTag(lastTag, maxTags);
|
||||
const parsedTag = this.tagService.parseTag(baseTag);
|
||||
|
||||
this.coreService.info(
|
||||
@@ -168,7 +181,9 @@ export class TagIncrementer {
|
||||
const incrementType = await this.determineIncrementType(ref);
|
||||
|
||||
const newTag = this.tagService.bumpTag(parsedTag, incrementType);
|
||||
const renderedTag = this.tagService.renderTag(newTag);
|
||||
|
||||
const prereleaseSuffix = prerelease && githubSha ? `-rc-${githubSha}` : "";
|
||||
const renderedTag = this.tagService.renderTag(newTag, "", prereleaseSuffix);
|
||||
|
||||
this.coreService.info(`New tag: ${renderedTag}`);
|
||||
this.coreService.info(
|
||||
@@ -242,12 +257,27 @@ export async function run(): Promise<void> {
|
||||
const coreService = new LocalCoreService();
|
||||
|
||||
const lastTag = coreService.getInput("last-tag");
|
||||
const maxTagsInput = coreService.getInput("max-tags");
|
||||
const maxTags = parseInt(maxTagsInput || "50", 10);
|
||||
const prereleaseInput = coreService.getInput("prerelease");
|
||||
const prerelease = prereleaseInput === "true";
|
||||
const githubSha = process.env.GITHUB_SHA || "";
|
||||
const ref = "HEAD";
|
||||
|
||||
if (prerelease && !githubSha) {
|
||||
const errorMessage =
|
||||
"Prerelease is enabled but GITHUB_SHA environment variable is not available. This is required for prerelease tags.";
|
||||
coreService.setFailed(errorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
// Log context information for debugging
|
||||
coreService.info(`Working with local git repository`);
|
||||
coreService.info(`Commit ref: ${ref}`);
|
||||
coreService.info(`Last tag input: ${lastTag || "undefined"}`);
|
||||
coreService.info(`Max tags limit: ${maxTags}`);
|
||||
coreService.info(`Prerelease: ${prerelease}`);
|
||||
coreService.info(`GitHub SHA: ${githubSha || "undefined"}`);
|
||||
|
||||
const gitService = new LocalGitService();
|
||||
const commitAnalyzer = new ConventionalCommitAnalyzer();
|
||||
@@ -261,7 +291,13 @@ export async function run(): Promise<void> {
|
||||
await gitService.testConnection();
|
||||
coreService.info("Git repository connection successful");
|
||||
|
||||
const newTag = await tagIncrementer.incrementTag(lastTag || undefined, ref);
|
||||
const newTag = await tagIncrementer.incrementTag(
|
||||
lastTag || undefined,
|
||||
ref,
|
||||
maxTags,
|
||||
prerelease,
|
||||
githubSha
|
||||
);
|
||||
|
||||
coreService.info(`Process completed successfully. New tag: ${newTag}`);
|
||||
coreService.setOutput("new-tag", newTag);
|
||||
|
||||
Reference in New Issue
Block a user