├── .github ├── CODEOWNERS ├── dependabot.yml ├── release-drafter.yml └── workflows │ ├── jenkins-security-scan.yml │ └── release-drafter.yml ├── .gitignore ├── .mvn ├── extensions.xml └── maven.config ├── Jenkinsfile ├── README.md ├── pom.xml └── src ├── main ├── java │ └── org │ │ └── jenkinsci │ │ └── plugins │ │ └── changeassemblyversion │ │ ├── AssemblyVersion.java │ │ ├── ChangeAssemblyVersion.java │ │ └── ChangeTools.java └── resources │ ├── index.jelly │ └── org │ └── jenkinsci │ └── plugins │ └── changeassemblyversion │ └── ChangeAssemblyVersion │ ├── config.jelly │ ├── help-assemblyFile.html │ ├── help-regexPattern.html │ ├── help-replacementPattern.html │ ├── help-task.html │ └── help.html └── test └── java └── org └── jenkinsci └── plugins └── changeassemblyversion └── ReplacementsTest.java /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @jenkinsci/change-assembly-version-plugin-developers 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: maven 4 | directory: "/" 5 | schedule: 6 | interval: monthly 7 | open-pull-requests-limit: 10 8 | target-branch: master 9 | labels: 10 | - dependencies 11 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | _extends: .github 2 | tag-template: change-assembly-version-plugin-$NEXT_PATCH_VERSION -------------------------------------------------------------------------------- /.github/workflows/jenkins-security-scan.yml: -------------------------------------------------------------------------------- 1 | name: Jenkins Security Scan 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | types: [ opened, synchronize, reopened ] 9 | workflow_dispatch: 10 | 11 | permissions: 12 | security-events: write 13 | contents: read 14 | actions: read 15 | 16 | jobs: 17 | security-scan: 18 | uses: jenkins-infra/jenkins-security-scan/.github/workflows/jenkins-security-scan.yaml@v2 19 | with: 20 | java-cache: 'maven' # Optionally enable use of a build dependency cache. Specify 'maven' or 'gradle' as appropriate. 21 | # java-version: 21 # Optionally specify what version of Java to set up for the build, or remove to use a recent default. 22 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | # branches to consider in the event; optional, defaults to all 6 | branches: 7 | - master 8 | 9 | jobs: 10 | update_release_draft: 11 | runs-on: ubuntu-latest 12 | steps: 13 | # Drafts your next Release notes as Pull Requests are merged into "master" 14 | - uses: release-drafter/release-drafter@v5 15 | env: 16 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.idea/ 3 | *.bak 4 | -------------------------------------------------------------------------------- /.mvn/extensions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | io.jenkins.tools.incrementals 4 | git-changelist-maven-extension 5 | 1.8 6 | 7 | 8 | -------------------------------------------------------------------------------- /.mvn/maven.config: -------------------------------------------------------------------------------- 1 | -Pconsume-incrementals 2 | -Pmight-produce-incrementals 3 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | /* 2 | * See the documentation for more options: 3 | * https://github.com/jenkins-infra/pipeline-library/ 4 | */ 5 | buildPlugin(useContainerAgent: true, configurations: [ 6 | [ platform: 'windows', jdk: '17' ], 7 | [ platform: 'linux', jdk: '17' ], 8 | [ platform: 'linux', jdk: '21' ], 9 | ]) 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # change-assembly-version-plugin 2 | 3 | This plug-in tries to change specific line(s) in file(s). The effect is to change the assembly version. 4 | 5 | Specifically, it will try to find the file `FileName` and inside that file look for strings matching the `RegexPattern`, replacing it with the actual current build number, as specified by `ReplacementPattern`. 6 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.jenkins-ci.plugins 6 | plugin 7 | 5.17 8 | 9 | 10 | org.jenkinsci.plugins 11 | change-assembly-version-plugin 12 | ${revision}${changelist} 13 | hpi 14 | 15 | https://wiki.jenkins-ci.org/display/JENKINS/Change+Assembly+Version 16 | 17 | 18 | 19 | MIT License 20 | http://opensource.org/licenses/MIT 21 | 22 | 23 | 24 | 25 | 26 | slide 27 | Alex Earl 28 | slide.o.mix@gmail.com 29 | 30 | 31 | 32 | 33 | scm:git:https://github.com/${gitHubRepo}.git 34 | scm:git:git@github.com:${gitHubRepo}.git 35 | ${scmTag} 36 | https://github.com/${gitHubRepo} 37 | 38 | 39 | 40 | 41 | false 42 | maven.jenkins-ci.org 43 | https://repo.jenkins-ci.org/releases/ 44 | 45 | 46 | maven.jenkins-ci.org 47 | https://repo.jenkins-ci.org/snapshots/ 48 | 49 | 50 | 51 | 52 | 1.11 53 | -SNAPSHOT 54 | jenkinsci/change-assembly-version-plugin 55 | 2.479 56 | ${jenkins.baseline}.1 57 | Max 58 | false 59 | Low 60 | 61 | 62 | 63 | 64 | 65 | io.jenkins.tools.bom 66 | bom-2.479.x 67 | 4862.vc32a_71c3e731 68 | pom 69 | import 70 | 71 | 72 | 73 | 74 | 75 | 76 | org.jenkins-ci.plugins 77 | structs 78 | 79 | 80 | org.bouncycastle 81 | bcprov-jdk15on 82 | 1.70 83 | test 84 | 85 | 86 | 87 | 88 | 89 | repo.jenkins-ci.org 90 | https://repo.jenkins-ci.org/public/ 91 | 92 | 93 | 94 | 95 | 96 | repo.jenkins-ci.org 97 | https://repo.jenkins-ci.org/public/ 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/changeassemblyversion/AssemblyVersion.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.changeassemblyversion; 2 | 3 | import hudson.EnvVars; 4 | import hudson.model.BuildListener; 5 | 6 | public class AssemblyVersion { 7 | 8 | private String version; 9 | private EnvVars envVars; 10 | private BuildListener listener; 11 | 12 | /** 13 | * The instance of this class gonna return in the property version the value to be used on ChangeTools. 14 | * @param version 15 | * @param envVars 16 | */ 17 | public AssemblyVersion(String version, EnvVars envVars) { 18 | this.envVars = envVars; 19 | 20 | this.version = envVars.expand(version); 21 | } 22 | 23 | public String getVersion() { 24 | return this.version; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/changeassemblyversion/ChangeAssemblyVersion.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.changeassemblyversion; 2 | 3 | import hudson.AbortException; 4 | import hudson.EnvVars; 5 | import hudson.Extension; 6 | import hudson.FilePath; 7 | import hudson.Launcher; 8 | import hudson.model.AbstractBuild; 9 | import hudson.model.AbstractProject; 10 | import hudson.model.BuildListener; 11 | import hudson.model.Run; 12 | import hudson.model.TaskListener; 13 | import hudson.tasks.BuildStepDescriptor; 14 | import hudson.tasks.Builder; 15 | import java.io.IOException; 16 | import java.io.PrintWriter; 17 | import java.io.StringWriter; 18 | import jenkins.tasks.SimpleBuildStep; 19 | import org.apache.commons.lang.StringUtils; 20 | import org.jenkinsci.Symbol; 21 | import org.kohsuke.stapler.DataBoundConstructor; 22 | import org.kohsuke.stapler.DataBoundSetter; 23 | 24 | /** 25 | * @author Leonardo Kobus 26 | */ 27 | public class ChangeAssemblyVersion extends Builder implements SimpleBuildStep { 28 | 29 | private final String versionPattern; 30 | private String assemblyFile; 31 | private String regexPattern; 32 | private String replacementPattern; 33 | private String assemblyTitle; 34 | private String assemblyDescription; 35 | private String assemblyCompany; 36 | private String assemblyProduct; 37 | private String assemblyCopyright; 38 | private String assemblyTrademark; 39 | private String assemblyCulture; 40 | 41 | @Deprecated 42 | public ChangeAssemblyVersion( 43 | String versionPattern, 44 | String assemblyFile, 45 | String regexPattern, 46 | String replacementPattern, 47 | String assemblyTitle, 48 | String assemblyDescription, 49 | String assemblyCompany, 50 | String assemblyProduct, 51 | String assemblyCopyright, 52 | String assemblyTrademark, 53 | String assemblyCulture) { 54 | this.versionPattern = versionPattern; 55 | this.assemblyFile = assemblyFile; 56 | this.regexPattern = regexPattern; 57 | this.replacementPattern = replacementPattern; 58 | this.assemblyTitle = assemblyTitle; 59 | this.assemblyDescription = assemblyDescription; 60 | this.assemblyCompany = assemblyCompany; 61 | this.assemblyProduct = assemblyProduct; 62 | this.assemblyCopyright = assemblyCopyright; 63 | this.assemblyTrademark = assemblyTrademark; 64 | this.assemblyCulture = assemblyCulture; 65 | } 66 | 67 | @DataBoundConstructor 68 | public ChangeAssemblyVersion(String versionPattern) { 69 | this.versionPattern = versionPattern; 70 | } 71 | 72 | @DataBoundSetter 73 | public void setAssemblyFile(String file) { 74 | this.assemblyFile = file; 75 | } 76 | 77 | @DataBoundSetter 78 | public void setRegexPattern(String regexPattern) { 79 | this.regexPattern = regexPattern; 80 | } 81 | 82 | @DataBoundSetter 83 | public void setReplacementPattern(String pattern) { 84 | this.replacementPattern = pattern; 85 | } 86 | 87 | @DataBoundSetter 88 | public void setAssemblyTitle(String title) { 89 | this.assemblyTitle = title; 90 | } 91 | 92 | @DataBoundSetter 93 | public void setAssemblyDescription(String description) { 94 | this.assemblyDescription = description; 95 | } 96 | 97 | @DataBoundSetter 98 | public void setAssemblyCompany(String company) { 99 | this.assemblyCompany = company; 100 | } 101 | 102 | @DataBoundSetter 103 | public void setAssemblyProduct(String product) { 104 | this.assemblyProduct = product; 105 | } 106 | 107 | @DataBoundSetter 108 | public void setAssemblyCopyright(String copyright) { 109 | this.assemblyCopyright = copyright; 110 | } 111 | 112 | @DataBoundSetter 113 | public void setAssemblyTrademark(String trademark) { 114 | this.assemblyTrademark = trademark; 115 | } 116 | 117 | @DataBoundSetter 118 | public void setAssemblyCulture(String culture) { 119 | this.assemblyCulture = culture; 120 | } 121 | 122 | public String getVersionPattern() { 123 | return this.versionPattern; 124 | } 125 | 126 | public String getAssemblyFile() { 127 | return this.assemblyFile; 128 | } 129 | 130 | public String getRegexPattern() { 131 | return this.regexPattern; 132 | } 133 | 134 | public String getReplacementPattern() { 135 | return this.replacementPattern; 136 | } 137 | 138 | public String getAssemblyTitle() { 139 | return this.assemblyTitle; 140 | } 141 | 142 | public String getAssemblyDescription() { 143 | return this.assemblyDescription; 144 | } 145 | 146 | public String getAssemblyCompany() { 147 | return this.assemblyCompany; 148 | } 149 | 150 | public String getAssemblyProduct() { 151 | return this.assemblyProduct; 152 | } 153 | 154 | public String getAssemblyCopyright() { 155 | return this.assemblyCopyright; 156 | } 157 | 158 | public String getAssemblyTrademark() { 159 | return this.assemblyTrademark; 160 | } 161 | 162 | public String getAssemblyCulture() { 163 | return this.assemblyCulture; 164 | } 165 | 166 | @Override 167 | public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) 168 | throws InterruptedException, IOException { 169 | try { 170 | perform(build, build.getWorkspace(), launcher, listener); 171 | } catch (AbortException ex) { 172 | return false; 173 | } catch (Exception ex) { 174 | StringWriter sw = new StringWriter(); 175 | ex.printStackTrace(new PrintWriter(sw)); 176 | listener.getLogger().println(sw.toString()); 177 | } 178 | 179 | return true; 180 | } 181 | 182 | /** 183 | * 184 | * The perform method is gonna search all the file named "Assemblyinfo.cs" 185 | * in any folder below, and after found will change the version of 186 | * AssemblyVersion and AssemblyFileVersion in the file for the inserted 187 | * version (versionPattern property value). 188 | * 189 | * 190 | * OBS: The inserted value can be some jenkins variable like ${BUILD_NUMBER} 191 | * just the variable alone, but not implemented to treat 192 | * 0.0.${BUILD_NUMBER}.0 I think this plugin must be used with Version 193 | * Number Plugin. 194 | * 195 | * 196 | */ 197 | @Override 198 | public void perform(Run run, FilePath workspace, Launcher launcher, TaskListener listener) 199 | throws InterruptedException, IOException { 200 | 201 | try { 202 | String assemblyGlob = this.assemblyFile == null || this.assemblyFile.equals("") 203 | ? "**/AssemblyInfo.cs" 204 | : this.assemblyFile; 205 | 206 | EnvVars envVars = run.getEnvironment(listener); 207 | 208 | if (run instanceof AbstractBuild build) { 209 | envVars.overrideAll(build.getBuildVariables()); 210 | } 211 | 212 | String version = new AssemblyVersion(this.versionPattern, envVars).getVersion(); 213 | if (versionPattern == null || StringUtils.isEmpty(versionPattern)) { 214 | listener.getLogger().println("Please provide a valid version pattern."); 215 | throw new AbortException("Please provide a valid version pattern."); 216 | } 217 | 218 | // Expand env variables 219 | String assemblyTitle = envVars.expand(this.assemblyTitle); 220 | String assemblyDescription = envVars.expand(this.assemblyDescription); 221 | String assemblyCompany = envVars.expand(this.assemblyCompany); 222 | String assemblyProduct = envVars.expand(this.assemblyProduct); 223 | String assemblyCopyright = envVars.expand(this.assemblyCopyright); 224 | String assemblyTrademark = envVars.expand(this.assemblyTrademark); 225 | String assemblyCulture = envVars.expand(this.assemblyCulture); 226 | 227 | // Log new expanded values 228 | listener.getLogger().println("Changing File(s): %s".formatted(assemblyGlob)); 229 | listener.getLogger().println("Assembly Version : %s".formatted(version)); 230 | listener.getLogger().println("Assembly Title : %s".formatted(assemblyTitle)); 231 | listener.getLogger().println("Assembly Description : %s".formatted(assemblyDescription)); 232 | listener.getLogger().println("Assembly Company : %s".formatted(assemblyCompany)); 233 | listener.getLogger().println("Assembly Product : %s".formatted(assemblyProduct)); 234 | listener.getLogger().println("Assembly Copyright : %s".formatted(assemblyCopyright)); 235 | listener.getLogger().println("Assembly Trademark : %s".formatted(assemblyTrademark)); 236 | listener.getLogger().println("Assembly Culture : %s".formatted(assemblyCulture)); 237 | 238 | for (FilePath f : workspace.list(assemblyGlob)) { 239 | // Update the AssemblyVerion and AssemblyFileVersion 240 | new ChangeTools(f, this.regexPattern, this.replacementPattern).Replace(version, listener); 241 | 242 | // Set new things, empty string being ok for them. 243 | // TODO: Would we need a regex for these or just blast as we are doing now? 244 | new ChangeTools(f, "AssemblyTitle[(]\".*\"[)]", "AssemblyTitle(\"%s\")") 245 | .Replace(assemblyTitle, listener); 246 | new ChangeTools(f, "AssemblyDescription[(]\".*\"[)]", "AssemblyDescription(\"%s\")") 247 | .Replace(assemblyDescription, listener); 248 | new ChangeTools(f, "AssemblyCompany[(]\".*\"[)]", "AssemblyCompany(\"%s\")") 249 | .Replace(assemblyCompany, listener); 250 | new ChangeTools(f, "AssemblyProduct[(]\".*\"[)]", "AssemblyProduct(\"%s\")") 251 | .Replace(assemblyProduct, listener); 252 | new ChangeTools(f, "AssemblyCopyright[(]\".*\"[)]", "AssemblyCopyright(\"%s\")") 253 | .Replace(assemblyCopyright, listener); 254 | new ChangeTools(f, "AssemblyTrademark[(]\".*\"[)]", "AssemblyTrademark(\"%s\")") 255 | .Replace(assemblyTrademark, listener); 256 | new ChangeTools(f, "AssemblyCulture[(]\".*\"[)]", "AssemblyCulture(\"%s\")") 257 | .Replace(assemblyCulture, listener); 258 | } 259 | } catch (Exception ex) { 260 | StringWriter sw = new StringWriter(); 261 | ex.printStackTrace(new PrintWriter(sw)); 262 | listener.getLogger().println(sw.toString()); 263 | 264 | throw new AbortException(sw.toString()); 265 | } 266 | } 267 | 268 | @Extension 269 | @Symbol("changeAsmVer") 270 | public static class DescriptorImpl extends BuildStepDescriptor { 271 | 272 | @Override 273 | public boolean isApplicable(Class jobType) { 274 | return true; 275 | } 276 | 277 | @Override 278 | public String getDisplayName() { 279 | return "Change Assembly Version"; 280 | } 281 | } 282 | } 283 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/changeassemblyversion/ChangeTools.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.changeassemblyversion; 2 | 3 | import hudson.FilePath; 4 | import hudson.model.TaskListener; 5 | import java.io.IOException; 6 | import java.io.OutputStream; 7 | import java.nio.charset.Charset; 8 | import org.apache.commons.io.ByteOrderMark; 9 | import org.apache.commons.io.IOUtils; 10 | import org.apache.commons.io.input.BOMInputStream; 11 | 12 | public class ChangeTools { 13 | 14 | private final FilePath file; 15 | private final String regexPattern; 16 | private final String replacementPattern; 17 | 18 | ChangeTools(FilePath f, String regexPattern, String replacementPattern) { 19 | this.file = f; 20 | if (regexPattern != null && !regexPattern.equals("")) { 21 | this.regexPattern = regexPattern; 22 | } else { 23 | this.regexPattern = "Version[(]\"[\\d\\.]+\"[)]"; 24 | } 25 | 26 | if (replacementPattern != null && !replacementPattern.equals("")) { 27 | this.replacementPattern = replacementPattern; 28 | } else { 29 | this.replacementPattern = "Version(\"%s\")"; 30 | } 31 | } 32 | 33 | public void Replace(String replacement, TaskListener listener) throws IOException, InterruptedException { 34 | if (replacement != null && !replacement.isEmpty()) { 35 | BOMInputStream inputStream = new BOMInputStream(file.read()); 36 | String content; 37 | ByteOrderMark bom; 38 | Charset fileEncoding = Charset.defaultCharset(); 39 | try { 40 | bom = inputStream.getBOM(); 41 | if (bom != null) { 42 | fileEncoding = Charset.forName(bom.getCharsetName()); 43 | } 44 | 45 | content = IOUtils.toString(inputStream, fileEncoding); 46 | } finally { 47 | inputStream.close(); 48 | } 49 | listener.getLogger() 50 | .println("Updating file : %s, Replacement : %s".formatted(file.getRemote(), replacement)); 51 | content = content.replaceAll(regexPattern, replacementPattern.formatted(replacement)); 52 | // listener.getLogger().println(String.format("Updating file : %s", file.getRemote())); 53 | OutputStream os = file.write(); 54 | try { 55 | if (bom != null) { 56 | os.write(bom.getBytes()); 57 | } 58 | os.write(content.getBytes(fileEncoding)); 59 | } finally { 60 | os.close(); 61 | } 62 | } else { 63 | listener.getLogger().println("Skipping replacement because value is empty.".formatted()); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/resources/index.jelly: -------------------------------------------------------------------------------- 1 | 2 |
3 | This plugin lets the user modify the AssemblyVersion and AssemblyFileVersion attributes, typically contained in the AssemblyInfo.cs file. 4 |
-------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/changeassemblyversion/ChangeAssemblyVersion/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/changeassemblyversion/ChangeAssemblyVersion/help-assemblyFile.html: -------------------------------------------------------------------------------- 1 |
2 | Set the file name to search. 3 | If the value is empty the default filename is : **/AssemblyInfo.cs. 4 | You can use an ant fileset pattern. 5 |
6 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/changeassemblyversion/ChangeAssemblyVersion/help-regexPattern.html: -------------------------------------------------------------------------------- 1 |
2 | Regex Pattern in which version is stated. 3 | Specify this value if you want to use for versioning other than assembly files (e.g. visual studio extensions .vsix). For assembly (.dll) versioning you do not need to specify anything here. 4 |
5 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/changeassemblyversion/ChangeAssemblyVersion/help-replacementPattern.html: -------------------------------------------------------------------------------- 1 |
2 | Version value specified in regexPattern will be replaced by this. Use if you want versioning other than assembly files. 3 | e.g. "Version(\"%s\")" where %s will be new version number. For assembly (.dll) versioning keep this field blank. 4 |
5 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/changeassemblyversion/ChangeAssemblyVersion/help-task.html: -------------------------------------------------------------------------------- 1 |
2 | Set the new version number for replace. 3 | Can be a variable like ${BUILD_NUMBER} or plain text (1.0.0.0), or a mix of the two. 4 |
5 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/changeassemblyversion/ChangeAssemblyVersion/help.html: -------------------------------------------------------------------------------- 1 |
2 | This build step can be used to change the version in the AssemblyInfo.cs file from a .NET Project, it's going to find every 3 | AssemblyInfo.cs file in the workspace and change the version from [AssemblyVersion] and [AssemblyFileVersion] attributes. 4 | It's recommended to use this plugin with the auto-generated AssemblyInfo.cs from VisualStudio. 5 |
6 | -------------------------------------------------------------------------------- /src/test/java/org/jenkinsci/plugins/changeassemblyversion/ReplacementsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright 2014 BELLINSALARIN. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | package org.jenkinsci.plugins.changeassemblyversion; 25 | 26 | import com.google.common.primitives.Bytes; 27 | import hudson.EnvVars; 28 | import hudson.Launcher; 29 | import hudson.model.AbstractBuild; 30 | import hudson.model.BuildListener; 31 | import hudson.model.FreeStyleBuild; 32 | import hudson.model.FreeStyleProject; 33 | import hudson.slaves.EnvironmentVariablesNodeProperty; 34 | import org.apache.commons.io.ByteOrderMark; 35 | import org.apache.commons.io.IOUtils; 36 | import org.apache.commons.io.input.BOMInputStream; 37 | import org.bouncycastle.util.Arrays; 38 | import org.junit.jupiter.api.Test; 39 | import org.jvnet.hudson.test.JenkinsRule; 40 | import org.jvnet.hudson.test.TestBuilder; 41 | import org.jvnet.hudson.test.junit.jupiter.WithJenkins; 42 | 43 | import java.io.IOException; 44 | import java.io.InputStream; 45 | import java.io.OutputStream; 46 | import java.io.OutputStreamWriter; 47 | import java.nio.charset.StandardCharsets; 48 | 49 | import static org.junit.jupiter.api.Assertions.assertEquals; 50 | import static org.junit.jupiter.api.Assertions.assertTrue; 51 | 52 | /** 53 | * @author BELLINSALARIN 54 | */ 55 | @WithJenkins 56 | class ReplacementsTest { 57 | 58 | @Test 59 | void testResolveEnvironmentVariables(JenkinsRule j) throws Exception { 60 | EnvironmentVariablesNodeProperty prop = new EnvironmentVariablesNodeProperty(); 61 | EnvVars envVars = prop.getEnvVars(); 62 | envVars.put("PREFIX", "1.1.0"); 63 | j.jenkins.getGlobalNodeProperties().add(prop); 64 | FreeStyleProject project = j.createFreeStyleProject(); 65 | project.getBuildersList().add(new TestBuilder() { 66 | public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) 67 | throws InterruptedException, IOException { 68 | build.getWorkspace() 69 | .child("AssemblyVersion.cs") 70 | .write( 71 | """ 72 | using System.Reflection; 73 | 74 | [assembly: AssemblyTitle("")] 75 | [assembly: AssemblyDescription("")] 76 | [assembly: AssemblyCompany("")] 77 | [assembly: AssemblyProduct("")] 78 | [assembly: AssemblyCopyright("")] 79 | [assembly: AssemblyTrademark("")] 80 | [assembly: AssemblyCulture("")] 81 | [assembly: AssemblyVersion("13.1.1.976")]""", 82 | "UTF-8"); 83 | return true; 84 | } 85 | }); 86 | ChangeAssemblyVersion builder = new ChangeAssemblyVersion( 87 | "$PREFIX.${BUILD_NUMBER}", 88 | "AssemblyVersion.cs", 89 | "", 90 | "", 91 | "MyTitle", 92 | "MyDescription", 93 | "MyCompany", 94 | "MyProduct", 95 | "MyCopyright", 96 | "MyTrademark", 97 | "MyCulture"); 98 | project.getBuildersList().add(builder); 99 | FreeStyleBuild build = project.scheduleBuild2(0).get(); 100 | 101 | // String s = FileUtils.readFileToString(build.getLogFile()); 102 | String content = build.getWorkspace().child("AssemblyVersion.cs").readToString(); 103 | assertTrue(content.contains("AssemblyVersion(\"1.1.0.")); 104 | 105 | // Check that we update additional assembly info 106 | assertTrue(content.contains("AssemblyTitle(\"MyTitle")); 107 | assertTrue(content.contains("AssemblyDescription(\"MyDescription")); 108 | assertTrue(content.contains("AssemblyCompany(\"MyCompany")); 109 | assertTrue(content.contains("AssemblyProduct(\"MyProduct")); 110 | assertTrue(content.contains("AssemblyCopyright(\"MyCopyright")); 111 | assertTrue(content.contains("AssemblyTrademark(\"MyTrademark")); 112 | assertTrue(content.contains("AssemblyCulture(\"MyCulture")); 113 | 114 | assertEquals("$PREFIX.${BUILD_NUMBER}", builder.getVersionPattern()); 115 | } 116 | 117 | @Test 118 | void testResolveEnvironmentVariables_recursively_excludingSvn(JenkinsRule j) 119 | throws Exception { 120 | EnvironmentVariablesNodeProperty prop = new EnvironmentVariablesNodeProperty(); 121 | EnvVars envVars = prop.getEnvVars(); 122 | envVars.put("PREFIX", "1.1.0"); 123 | j.jenkins.getGlobalNodeProperties().add(prop); 124 | FreeStyleProject project = j.createFreeStyleProject(); 125 | final String f1 = "myassembly/properties/AssemblyInfo.cs"; 126 | final String f2 = ".svn/myassembly/properties/AssemblyInfo.cs"; 127 | final String c = """ 128 | using System.Reflection; 129 | 130 | [assembly: AssemblyVersion("13.1.1.976")]"""; 131 | project.getBuildersList().add(new TestBuilder() { 132 | public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) 133 | throws InterruptedException, IOException { 134 | build.getWorkspace().child(f1).write(c, "UTF-8"); 135 | build.getWorkspace().child(f2).write(c, "UTF-8"); 136 | return true; 137 | } 138 | }); 139 | ChangeAssemblyVersion builder = 140 | new ChangeAssemblyVersion("$PREFIX.${BUILD_NUMBER}", "", "", "", "", "", "", "", "", "", ""); 141 | project.getBuildersList().add(builder); 142 | FreeStyleBuild build = project.scheduleBuild2(0).get(); 143 | 144 | // String s = FileUtils.readFileToString(build.getLogFile()); 145 | String content = build.getWorkspace().child(f1).readToString(); 146 | assertTrue(content.contains("AssemblyVersion(\"1.1.0.")); 147 | content = build.getWorkspace().child(f2).readToString(); 148 | assertTrue(content.contains("AssemblyVersion(\"13.1.1.976")); 149 | assertEquals("$PREFIX.${BUILD_NUMBER}", builder.getVersionPattern()); 150 | } 151 | 152 | @Test 153 | void testBOMFileReplace(JenkinsRule j) throws Exception { 154 | FreeStyleProject project = j.createFreeStyleProject(); 155 | final ByteOrderMark fileBom = ByteOrderMark.UTF_8; 156 | project.getBuildersList().add(new TestBuilder() { 157 | public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) 158 | throws InterruptedException, IOException { 159 | OutputStream outputStream = 160 | build.getWorkspace().child("AssemblyVersion.cs").write(); 161 | outputStream.write(fileBom.getBytes()); 162 | OutputStreamWriter w = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8); 163 | try { 164 | w.write( 165 | """ 166 | using System.Reflection; 167 | 168 | [assembly: AssemblyTitle("")] 169 | [assembly: AssemblyDescription("")] 170 | [assembly: AssemblyCompany("")] 171 | [assembly: AssemblyProduct("")] 172 | [assembly: AssemblyCopyright("")] 173 | [assembly: AssemblyTrademark("")] 174 | [assembly: AssemblyCulture("")] 175 | [assembly: AssemblyVersion("13.1.1.976")]"""); 176 | } finally { 177 | w.close(); 178 | outputStream.close(); 179 | } 180 | 181 | return true; 182 | } 183 | }); 184 | ChangeAssemblyVersion builder = new ChangeAssemblyVersion( 185 | "1.2.3", 186 | "AssemblyVersion.cs", 187 | "", 188 | "", 189 | "MyTitle", 190 | "MyDescription", 191 | "MyCompany", 192 | "MyProduct", 193 | "MyCopyright", 194 | "MyTrademark", 195 | "MyCulture"); 196 | project.getBuildersList().add(builder); 197 | FreeStyleBuild build = project.scheduleBuild2(0).get(); 198 | 199 | // String s = FileUtils.readFileToString(build.getLogFile()); 200 | InputStream readStream = 201 | build.getWorkspace().child("AssemblyVersion.cs").read(); 202 | BOMInputStream bomInputStream = BOMInputStream.builder().setInputStream(readStream).get(); 203 | ByteOrderMark bomAfterChange = bomInputStream.getBOM(); 204 | String content = org.apache.commons.io.IOUtils.toString(bomInputStream, StandardCharsets.UTF_8); 205 | bomInputStream.close(); 206 | readStream.close(); 207 | 208 | // the replaced file should have the same BOM as the origin. 209 | assertEquals(bomAfterChange.getCharsetName(), fileBom.getCharsetName()); 210 | // the first bytes should be readable characters. 211 | assertTrue(content.matches("(?s)^using.*")); 212 | // Check that we update additional assembly info 213 | assertTrue(content.contains("AssemblyTitle(\"MyTitle")); 214 | assertTrue(content.contains("AssemblyDescription(\"MyDescription")); 215 | assertTrue(content.contains("AssemblyCompany(\"MyCompany")); 216 | assertTrue(content.contains("AssemblyProduct(\"MyProduct")); 217 | assertTrue(content.contains("AssemblyCopyright(\"MyCopyright")); 218 | assertTrue(content.contains("AssemblyTrademark(\"MyTrademark")); 219 | assertTrue(content.contains("AssemblyCulture(\"MyCulture")); 220 | } 221 | 222 | @Test 223 | void testBOMFileNotModified(JenkinsRule j) throws Exception { 224 | FreeStyleProject project = j.createFreeStyleProject(); 225 | final byte[] originFileBinary = 226 | (""" 227 | using System.Reflection; 228 | 229 | [assembly: AssemblyTitle("")] 230 | [assembly: AssemblyDescription("")] 231 | [assembly: AssemblyCompany("")] 232 | [assembly: AssemblyProduct("")] 233 | [assembly: AssemblyCopyright("")] 234 | [assembly: AssemblyTrademark("")] 235 | [assembly: AssemblyCulture("")] 236 | [assembly: AssemblyVersion("13.1.1.976")]""") 237 | .getBytes(); 238 | project.getBuildersList().add(new TestBuilder() { 239 | public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) 240 | throws InterruptedException, IOException { 241 | try (OutputStream outputStream = build.getWorkspace().child("AssemblyVersion.cs").write()) { 242 | outputStream.write(originFileBinary); 243 | } 244 | 245 | return true; 246 | } 247 | }); 248 | 249 | ChangeAssemblyVersion builder = new ChangeAssemblyVersion(""); 250 | project.getBuildersList().add(builder); 251 | FreeStyleBuild build = project.scheduleBuild2(0).get(); 252 | 253 | // String s = FileUtils.readFileToString(build.getLogFile()); 254 | InputStream readStream = 255 | build.getWorkspace().child("AssemblyVersion.cs").read(); 256 | 257 | byte[] binaryAfterChange = IOUtils.toByteArray(readStream); 258 | String content = new String(binaryAfterChange, StandardCharsets.UTF_8); 259 | 260 | // the replaced file should have the same BOM as the origin. 261 | assertTrue(Arrays.areEqual(originFileBinary, binaryAfterChange)); 262 | } 263 | 264 | @Test 265 | void testBOMFileNotModified2(JenkinsRule j) throws Exception { 266 | FreeStyleProject project = j.createFreeStyleProject(); 267 | ByteOrderMark bom = ByteOrderMark.UTF_8; 268 | byte[] bomBinary = bom.getBytes(); 269 | final byte[] originFileContentBinary = 270 | (""" 271 | using System.Reflection; 272 | 273 | [assembly: AssemblyTitle("")] 274 | [assembly: AssemblyDescription("")] 275 | [assembly: AssemblyCompany("")] 276 | [assembly: AssemblyProduct("")] 277 | [assembly: AssemblyCopyright("")] 278 | [assembly: AssemblyTrademark("")] 279 | [assembly: AssemblyCulture("")] 280 | [assembly: AssemblyVersion("13.1.1.976")]""") 281 | .getBytes(); 282 | final byte[] originFileBinary = Bytes.concat(bomBinary, originFileContentBinary); 283 | project.getBuildersList().add(new TestBuilder() { 284 | public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) 285 | throws InterruptedException, IOException { 286 | try (OutputStream outputStream = build.getWorkspace().child("AssemblyVersion.cs").write()) { 287 | outputStream.write(originFileBinary); 288 | } 289 | 290 | return true; 291 | } 292 | }); 293 | 294 | ChangeAssemblyVersion builder = new ChangeAssemblyVersion(""); 295 | project.getBuildersList().add(builder); 296 | FreeStyleBuild build = project.scheduleBuild2(0).get(); 297 | 298 | // String s = FileUtils.readFileToString(build.getLogFile()); 299 | InputStream readStream = 300 | build.getWorkspace().child("AssemblyVersion.cs").read(); 301 | byte[] binaryAfterChange = IOUtils.toByteArray(readStream); 302 | readStream.close(); 303 | 304 | // the replaced file should have the same BOM as the origin. 305 | assertTrue(Arrays.areEqual(originFileBinary, binaryAfterChange)); 306 | } 307 | } 308 | --------------------------------------------------------------------------------