|
| 1 | +package testtools |
| 2 | + |
| 3 | +import ( |
| 4 | +"bytes" |
| 5 | +"flag" |
| 6 | +"fmt" |
| 7 | +"io" |
| 8 | +"log" |
| 9 | +"os" |
| 10 | +"os/exec" |
| 11 | +"path/filepath" |
| 12 | +"strconv" |
| 13 | +"strings" |
| 14 | +"testing" |
| 15 | + |
| 16 | +"github.com/stretchr/testify/require" |
| 17 | +) |
| 18 | + |
| 19 | +vararch=flag.String("arch","amd64","target architecture") |
| 20 | + |
| 21 | +funcTestStableToUnstable(t*testing.T) { |
| 22 | +fmt.Printf("***** ARCH %s *****\n",*arch) |
| 23 | +tagAppCli:=FetchDebPackage(t,"arduino-app-cli","latest",*arch) |
| 24 | +FetchDebPackage(t,"arduino-router","latest",*arch) |
| 25 | +majorTag:=majorTag(t,tagAppCli) |
| 26 | +_=minorTag(t,tagAppCli) |
| 27 | + |
| 28 | +fmt.Printf("Updating from stable version %s to unstable version %s\n",tagAppCli,majorTag) |
| 29 | +fmt.Printf("Building local deb version %s\n",majorTag) |
| 30 | +buildDebVersion(t,majorTag,*arch) |
| 31 | + |
| 32 | +fmt.Println("**** BUILD docker image *****") |
| 33 | +buildDockerImage(t,"test.Dockerfile","apt-test-update-image",*arch) |
| 34 | +fmt.Println("**** RUN docker image *****") |
| 35 | +runDockerCommand(t,"apt-test-update-image") |
| 36 | +preUpdateVersion:=runDockerSystemVersion(t,"apt-test-update") |
| 37 | +runDockerSystemUpdate(t,"apt-test-update") |
| 38 | +postUpdateVersion:=runDockerSystemVersion(t,"apt-test-update") |
| 39 | +runDockerCleanUp(t,"apt-test-update") |
| 40 | +require.Equal(t,preUpdateVersion,"Arduino App CLI "+tagAppCli) |
| 41 | +require.Equal(t,postUpdateVersion,"Arduino App CLI "+majorTag) |
| 42 | + |
| 43 | +} |
| 44 | + |
| 45 | +// func TestUnstableToStable(t *testing.T) { |
| 46 | +// tagAppCli := FetchDebPackage(t, "arduino-app-cli", "latest", *arch) |
| 47 | +// FetchDebPackage(t, "arduino-router", "latest", *arch) |
| 48 | +// minorTag := minorTag(t, tagAppCli) |
| 49 | +// moveDeb(t, "build/stable", "build/", "arduino-app-cli", tagAppCli, *arch) |
| 50 | + |
| 51 | +// fmt.Printf("Updating from unstable version %s to stable version %s \n", minorTag, tagAppCli) |
| 52 | +// fmt.Printf("Building local deb version %s \n", minorTag) |
| 53 | +// buildDebVersion(t, minorTag, *arch) |
| 54 | +// moveDeb(t, "build/", "build/stable", "arduino-app-cli", tagAppCli, *arch) |
| 55 | + |
| 56 | +// fmt.Println("**** BUILD docker image *****") |
| 57 | +// buildDockerImage(t, "test.Dockerfile", "test-apt-update") |
| 58 | +// fmt.Println("**** RUN docker image *****") |
| 59 | +// runDockerCommand(t, "test-apt-update") |
| 60 | +// preUpdateVersion := runDockerSystemVersion(t, "apt-test-update") |
| 61 | +// runDockerSystemUpdate(t, "apt-test-update") |
| 62 | +// postUpdateVersion := runDockerSystemVersion(t, "apt-test-update") |
| 63 | +// runDockerCleanUp(t, "apt-test-update") |
| 64 | +// require.Equal(t, preUpdateVersion, "Arduino App CLI "+tagAppCli) |
| 65 | +// require.Equal(t, postUpdateVersion, "Arduino App CLI "+minorTag) |
| 66 | + |
| 67 | +// } |
| 68 | + |
| 69 | +funcFetchDebPackage(t*testing.T,repo,version,archstring)string { |
| 70 | +t.Helper() |
| 71 | + |
| 72 | +cmd:=exec.Command( |
| 73 | +"gh","release","list", |
| 74 | +"--repo","github.com/arduino/"+repo, |
| 75 | +"--exclude-pre-releases", |
| 76 | +"--limit","1", |
| 77 | +) |
| 78 | + |
| 79 | +output,err:=cmd.CombinedOutput() |
| 80 | +iferr!=nil { |
| 81 | +log.Fatalf("command failed: %v\nOutput: %s",err,output) |
| 82 | +} |
| 83 | + |
| 84 | +fmt.Println(string(output)) |
| 85 | + |
| 86 | +fields:=strings.Fields(string(output)) |
| 87 | +iflen(fields)==0 { |
| 88 | +log.Fatal("could not parse tag from gh release list output") |
| 89 | +} |
| 90 | +tag:=fields[0] |
| 91 | +tagPath:=strings.TrimPrefix(tag,"v") |
| 92 | + |
| 93 | +debFile:=fmt.Sprintf("build/stable/%s_%s-1_%s.deb",repo,tagPath,arch) |
| 94 | +fmt.Println(debFile) |
| 95 | +if_,err:=os.Stat(debFile);err==nil { |
| 96 | +fmt.Printf("✅ %s already exists, skipping download.\n",debFile) |
| 97 | +returntag |
| 98 | +} |
| 99 | +fmt.Println("Detected tag:",tag) |
| 100 | +cmd2:=exec.Command( |
| 101 | +"gh","release","download", |
| 102 | +tag, |
| 103 | +"--repo","github.com/arduino/"+repo, |
| 104 | +"--pattern","*", |
| 105 | +"--dir","./build/stable", |
| 106 | +) |
| 107 | + |
| 108 | +out,err:=cmd2.CombinedOutput() |
| 109 | +iferr!=nil { |
| 110 | +log.Fatalf("download failed: %v\nOutput: %s",err,out) |
| 111 | +} |
| 112 | + |
| 113 | +returntag |
| 114 | + |
| 115 | +} |
| 116 | + |
| 117 | +funcbuildDebVersion(t*testing.T,tagVersion,archstring) { |
| 118 | +t.Helper() |
| 119 | +cwd,err:=os.Getwd() |
| 120 | +iferr!=nil { |
| 121 | +panic(err) |
| 122 | +} |
| 123 | +outputDir:=filepath.Join(cwd,"build") |
| 124 | + |
| 125 | +cmd:=exec.Command( |
| 126 | +"go","tool","task","build-deb", |
| 127 | +fmt.Sprintf("VERSION=%s",tagVersion), |
| 128 | +fmt.Sprintf("ARCH=%s",arch), |
| 129 | +fmt.Sprintf("OUTPUT=%s",outputDir), |
| 130 | +) |
| 131 | + |
| 132 | +iferr:=cmd.Run();err!=nil { |
| 133 | +log.Fatalf("failed to run build command: %v",err) |
| 134 | +} |
| 135 | +} |
| 136 | + |
| 137 | +funcmajorTag(t*testing.T,tagstring)string { |
| 138 | +t.Helper() |
| 139 | + |
| 140 | +parts:=strings.Split(tag,".") |
| 141 | +last:=parts[len(parts)-1] |
| 142 | + |
| 143 | +// Remove potential prefix 'v' from the first part, but not from the patch |
| 144 | +lastNum,_:=strconv.Atoi(strings.TrimPrefix(last,"v")) |
| 145 | +lastNum++ |
| 146 | + |
| 147 | +parts[len(parts)-1]=strconv.Itoa(lastNum) |
| 148 | +newTag:=strings.Join(parts,".") |
| 149 | + |
| 150 | +returnnewTag |
| 151 | +} |
| 152 | + |
| 153 | +funcminorTag(t*testing.T,tagstring)string { |
| 154 | +t.Helper() |
| 155 | + |
| 156 | +parts:=strings.Split(tag,".") |
| 157 | +last:=parts[len(parts)-1] |
| 158 | + |
| 159 | +lastNum,_:=strconv.Atoi(strings.TrimPrefix(last,"v")) |
| 160 | +iflastNum>0 { |
| 161 | +lastNum-- |
| 162 | +} |
| 163 | + |
| 164 | +parts[len(parts)-1]=strconv.Itoa(lastNum) |
| 165 | +newTag:=strings.Join(parts,".") |
| 166 | + |
| 167 | +if!strings.HasPrefix(newTag,"v") { |
| 168 | +newTag="v"+newTag |
| 169 | +} |
| 170 | +returnnewTag |
| 171 | +} |
| 172 | + |
| 173 | +funcbuildDockerImage(t*testing.T,dockerfile,name,archstring) { |
| 174 | +t.Helper() |
| 175 | + |
| 176 | +cmd:=exec.Command("docker","build","--build-arg","ARCH="+arch,"-t",name,"-f",dockerfile,".") |
| 177 | +out,err:=cmd.CombinedOutput() |
| 178 | +iferr!=nil { |
| 179 | +t.Fatalf("docker build failed: %v\nOutput:\n%s",err,string(out)) |
| 180 | +} |
| 181 | + |
| 182 | +} |
| 183 | + |
| 184 | +funcrunDockerCommand(t*testing.T,containerImageNamestring) { |
| 185 | +t.Helper() |
| 186 | + |
| 187 | +cmd:=exec.Command( |
| 188 | +"docker","run","--rm","-d", |
| 189 | +"--privileged", |
| 190 | +"--cgroupns=host", |
| 191 | +"-v","/sys/fs/cgroup:/sys/fs/cgroup:rw", |
| 192 | +"-v","/var/run/docker.sock:/var/run/docker.sock", |
| 193 | +"-e","DOCKER_HOST=unix:///var/run/docker.sock", |
| 194 | +"--name","apt-test-update", |
| 195 | +containerImageName, |
| 196 | +) |
| 197 | + |
| 198 | +iferr:=cmd.Run();err!=nil { |
| 199 | +t.Fatalf("failed to run container: %v",err) |
| 200 | +} |
| 201 | + |
| 202 | +} |
| 203 | + |
| 204 | +funcrunDockerSystemVersion(t*testing.T,containerNamestring)string { |
| 205 | +t.Helper() |
| 206 | + |
| 207 | +cmd:=exec.Command( |
| 208 | +"docker","exec", |
| 209 | +"--user","arduino", |
| 210 | +containerName, |
| 211 | +"arduino-app-cli","version", |
| 212 | +) |
| 213 | + |
| 214 | +output,err:=cmd.CombinedOutput() |
| 215 | +iferr!=nil { |
| 216 | +log.Fatalf("command failed: %v\nOutput: %s",err,output) |
| 217 | +} |
| 218 | + |
| 219 | +returnstring(output) |
| 220 | + |
| 221 | +} |
| 222 | + |
| 223 | +funcrunDockerSystemUpdate(t*testing.T,containerNamestring) { |
| 224 | +t.Helper() |
| 225 | +varbuf bytes.Buffer |
| 226 | + |
| 227 | +cmd:=exec.Command( |
| 228 | +"docker","exec", |
| 229 | +containerName, |
| 230 | +"sh","-lc", |
| 231 | +`su - arduino -c "yes | arduino-app-cli system update"`, |
| 232 | +) |
| 233 | + |
| 234 | +cmd.Stdout=io.MultiWriter(os.Stdout,&buf) |
| 235 | + |
| 236 | +iferr:=cmd.Run();err!=nil { |
| 237 | +fmt.Fprintf(os.Stderr,"Error running system update: %v\n",err) |
| 238 | +os.Exit(1) |
| 239 | +} |
| 240 | + |
| 241 | +} |
| 242 | + |
| 243 | +funcrunDockerCleanUp(t*testing.T,containerNamestring) { |
| 244 | +t.Helper() |
| 245 | + |
| 246 | +cleanupCmd:=exec.Command("docker","rm","-f",containerName) |
| 247 | + |
| 248 | +fmt.Println("🧹 Removing Docker container "+containerName) |
| 249 | +iferr:=cleanupCmd.Run();err!=nil { |
| 250 | +fmt.Printf("⚠️ Warning: could not remove container (might not exist): %v\n",err) |
| 251 | +} |
| 252 | + |
| 253 | +} |
| 254 | + |
| 255 | +funcmoveDeb(t*testing.T,startDir,targetDir,repostring,tagVersionstring,archstring) { |
| 256 | +t.Helper() |
| 257 | +tagPath:=strings.TrimPrefix(tagVersion,"v") |
| 258 | + |
| 259 | +debFile:=fmt.Sprintf("%s/%s_%s-1_%s.deb",startDir,repo,tagPath,arch) |
| 260 | + |
| 261 | +moveCmd:=exec.Command("cp",debFile,targetDir) |
| 262 | + |
| 263 | +fmt.Printf("📦 Moving %s → %s\n",debFile,targetDir) |
| 264 | +iferr:=moveCmd.Run();err!=nil { |
| 265 | +panic(fmt.Errorf("failed to move deb file: %w",err)) |
| 266 | +} |
| 267 | +} |