3

In my android project I got very intericting task My company wants to hide all mintions about her in code (variables names, packages and etc) But only for one flavour, so I cannot do it once for all project.

My first idea, was writing a simple gradle task, that will replace all strings that fit in code, but in this case package names will remain unchanged.

Secondly, since we have ci on Jenkins, I thought about jenkins script, that will rename all files and its content, if it has keyword. But this solution looks very bulky for me.

Maybe there is another, elegant way?

Andrej Istomin
  • 2,527
  • 2
  • 15
  • 22
Vitaliy
  • 485
  • 1
  • 5
  • 22

2 Answers2

1

Replacing the package name/variable names blindly seems a bit risky, as it could replace other overlapping strings as well, which may lead to various issues. Assuming your package name is unique and you don't have any overlapping names and it doesn't result in any directory name changes, you can use different methods to replace the package name.

Option 1

Using shell to achieve this. There are plenty of different ways to do this, following is one option with grep and sed

sh '''
grep -rl ${PACKAGE_NAME_TO_REPLACE} ${DESTINATION_DIR} | xargs sed -i "s&${PACKAGE_NAME_TO_REPLACE}&${PACKAGE_NAME_NEW}&g"
'''

You can take a look at this and this to understand different methods you can use.

Option 2

If you want a more controlled approach, you can achieve this with some groovy code as well. Simply run the following within your Pipeline.

def dirToSearchIn = "/where/to/replace"

// Change the content on specific files. You can improve the regex pattern below to fine-tune it. With the following pattern only files with extensions .java and .md will be changed.  
def filterFilePattern = ~/.*\.java|.*\.md$/

def oldString = "replaceme"
def newString = "newme"

new File(dirToSearchIn).traverse(type: groovy.io.FileType.FILES, nameFilter: filterFilePattern) { file ->
  println "Processing file: " + file.getPath()
  def fileContent = file.text;
  if (fileContent.contains(oldString)) {
    println "Replacing the content of the file: " + file.getPath()
    file.write(fileContent.replaceAll(oldString, newString));
  } else {
    println "Skipping file: " + file.getPath()
  }
}
ycr
  • 12,828
  • 2
  • 25
  • 45
  • 1
    This may break for Java package names and the like where there is a correspondence between directory names and file contents. – Chriki Dec 04 '22 at 21:51
  • @Chriki Agreed. That's mentioned under assumptions. If the package renaming introduces directory names, there is additional work. – ycr Dec 04 '22 at 23:06
0

Adding something like the following to your top-level build.gradle file should do the trick (assuming your company is called “ACME” and you rather want it to be called “foobar”):

def duplicateProjDirName = 'duplicateProj'
def duplicateProjDir = project.layout.buildDirectory.dir(duplicateProjDirName)

def duplicateProj = tasks.register('createDuplicateProject', Copy) {
    enabled = (projectDir.name != duplicateProjDirName)

    from(project.layout.projectDirectory)
    into(duplicateProjDir)
    exclude('build', '.gradle')

    def acmePattern = /(?i)acme/
    def newCompanyName = 'foobar'
    eachFile { it.path = it.sourcePath.replaceAll(acmePattern, newCompanyName) }
    filter { it.replaceAll(acmePattern, newCompanyName) }
    includeEmptyDirs = false
}

def duplicateBuild = tasks.register('buildDuplicateProject', GradleBuild) {
    enabled = (projectDir.name != duplicateProjDirName)

    dependsOn(duplicateProj)

    dir = duplicateProjDir
    tasks = ['build']
}

tasks.named('build').configure {
    dependsOn(duplicateBuild)
}

This essentially adds two tasks to the project:

  • createDuplicateProject duplicates the project under build/duplicateProj/ with all mentions of “ACME” replaced with “foobar”. It also takes care of renaming files/directories (in contrast to the solutions in other answers so far).
  • buildDuplicateProject builds the duplicate project.

While this may work in basic scenarios (I’ve successfully tested it with a small dummy Java project and Gradle 7.6), there are some edge cases to think about:

  • There may be dependencies (libraries, services, etc.) which contain the company name and which won’t work anymore after they’ve been renamed.
  • This way of replacing
    • may not work well for binary files.
    • does not catch occurrences in code such as "AC" + "ME".
    • case-insensitively may lead to weird-looking names that don’t follow common conventions. In the worst case, this could lead to different behavior, too.
  • There may be downstream projects which depend on package names or the like that are renamed here.
  • Your company may not only be identifiable by name, e.g., there may also be logos in image files, etc.
  • and probably others
Chriki
  • 15,638
  • 3
  • 51
  • 66