serverless-plugin-go 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.cz.toml CHANGED
@@ -1,7 +1,7 @@
1
1
  [tool]
2
2
  [tool.commitizen]
3
3
  name = "cz_customize"
4
- version = "0.1.2"
4
+ version = "0.1.4"
5
5
  tag_format = "v$version"
6
6
  bump_message = "bump: release $current_version → $new_version [skip-ci]"
7
7
  update_changelog_on_bump = true
package/CHANGELOG.md CHANGED
@@ -1,3 +1,20 @@
1
+ ## v0.1.4 (2026-11-13)
2
+
3
+
4
+ - fix: default GOFLAGS=-trimpath -buildvcs=false and merge custom env with defaults (#14)
5
+
6
+ ## v0.1.3 (2026-11-10)
7
+
8
+
9
+ - fix: per-function GOARCH and deterministic zip artifacts (#12)
10
+ - - Build env is now computed per function with an explicit GOARCH
11
+ (arm64/amd64). Previously config.env was mutated, so once an arm64
12
+ function compiled, every following x86_64 function was built as ARM,
13
+ and x86_64 builds on Apple Silicon used the host arch.
14
+ - Zip entries use a fixed mtime so identical binaries yield
15
+ byte-identical artifacts (stable hashes across builds).
16
+ - Bump mocha 10 -> 12 (mocha 10 fails to start on Node 26).
17
+
1
18
  ## v0.1.2 (2026-03-31)
2
19
 
3
20
 
package/README.md CHANGED
@@ -62,6 +62,9 @@ custom:
62
62
  env:
63
63
  GOOS: 'linux' # Default compile OS
64
64
  CGO_ENABLED: '0' # By default CGO is disabled
65
+ GOFLAGS: '-trimpath -buildvcs=false' # Reproducible builds by default
66
+ # Variables you define are merged with these defaults; redefining one (e.g. GOFLAGS) replaces
67
+ # its whole value. These values also override any same-named variable from the shell.
65
68
 
66
69
  # Bash commands to execute before compilation
67
70
  # These commands will be executed once before all compilations
package/index.js CHANGED
@@ -12,6 +12,9 @@ const supportedRuntimes = ["provided.al2", "provided.al2023"];
12
12
 
13
13
  const logPrefix = "GoPlugin";
14
14
 
15
+ // DOS zip timestamps can't go before 1980.
16
+ const zipEpoch = new Date(1980, 0, 1);
17
+
15
18
  export default class Go {
16
19
  constructor(serverless, options) {
17
20
  this.readFileSync = readFileSync;
@@ -27,6 +30,7 @@ export default class Go {
27
30
  env: {
28
31
  CGO_ENABLED: "0",
29
32
  GOOS: "linux",
33
+ GOFLAGS: "-trimpath -buildvcs=false", // reproducible builds
30
34
  },
31
35
  cmd: 'go build -ldflags="-s -w"',
32
36
  monorepo: false,
@@ -149,9 +153,12 @@ export default class Go {
149
153
  return;
150
154
  }
151
155
 
152
- if (arch === "arm64") {
153
- config.env["GOARCH"] = "arm64";
154
- }
156
+ // Per-function env: never mutate shared config, and always set GOARCH so the
157
+ // host arch (e.g. Apple Silicon) never leaks into an x86_64 Lambda.
158
+ const env = {
159
+ ...config.env,
160
+ GOARCH: arch === "arm64" ? "arm64" : "amd64",
161
+ };
155
162
 
156
163
  const absHandler = path.resolve(config.baseDir);
157
164
  const absBin = path.resolve(config.binDir);
@@ -172,7 +179,7 @@ export default class Go {
172
179
  compileBinPath = path.relative(cwd, compileBinPath);
173
180
  }
174
181
 
175
- this.logDebug(`[${name}] env: ${JSON.stringify(config.env)}`);
182
+ this.logDebug(`[${name}] env: ${JSON.stringify(env)}`);
176
183
  this.logDebug(`[${name}] cwd: ${cwd}`);
177
184
  this.logDebug(`[${name}] arch: ${arch}`);
178
185
 
@@ -181,7 +188,7 @@ export default class Go {
181
188
  compileBinPath,
182
189
  handler,
183
190
  cwd,
184
- config.env,
191
+ env,
185
192
  arch,
186
193
  );
187
194
 
@@ -261,6 +268,8 @@ export default class Go {
261
268
  */
262
269
  packageBootstrap(name, binPath) {
263
270
  this.zip.addFile("bootstrap", this.readFileSync(binPath), "", 0o755);
271
+ // Fixed mtime so identical binaries produce byte-identical zips (stable hashes).
272
+ this.zip.getEntry("bootstrap").header.time = zipEpoch;
264
273
 
265
274
  const zipPath = binPath + ".zip";
266
275
  this.zip.writeZip(zipPath);
@@ -283,7 +292,11 @@ export default class Go {
283
292
  const service = this.serverless.service;
284
293
 
285
294
  if (service.custom && service.custom.go) {
286
- config = { ...config, ...service.custom.go };
295
+ config = {
296
+ ...config,
297
+ ...service.custom.go,
298
+ env: { ...config.env, ...service.custom.go.env },
299
+ };
287
300
  }
288
301
 
289
302
  for (let env of Object.keys(config.env)) {
package/index.test.js CHANGED
@@ -2,6 +2,11 @@ import sinon from "sinon";
2
2
  import chai from "chai";
3
3
  import sinonChai from "sinon-chai";
4
4
 
5
+ import AdmZip from "adm-zip";
6
+ import fs from "fs";
7
+ import os from "os";
8
+ import path from "path";
9
+
5
10
  import Go from "./index.js";
6
11
  import ServerlessMock from "./mocks/serverless.mock.js";
7
12
 
@@ -27,6 +32,7 @@ describe("Go Plugin", () => {
27
32
  admZipStub = {
28
33
  addFile: sinon.stub(),
29
34
  writeZip: sinon.stub(),
35
+ getEntry: sinon.stub().returns({ header: {} }),
30
36
  };
31
37
  });
32
38
 
@@ -86,7 +92,15 @@ describe("Go Plugin", () => {
86
92
  `go build -ldflags="-s -w" -o .bin/testFunc2 functions/func2/main.go`,
87
93
  {
88
94
  cwd: ".",
89
- env: { ...process.env, ...{ CGO_ENABLED: "0", GOOS: "linux" } },
95
+ env: {
96
+ ...process.env,
97
+ ...{
98
+ CGO_ENABLED: "0",
99
+ GOOS: "linux",
100
+ GOFLAGS: "-trimpath -buildvcs=false",
101
+ GOARCH: "amd64",
102
+ },
103
+ },
90
104
  },
91
105
  );
92
106
 
@@ -98,7 +112,15 @@ describe("Go Plugin", () => {
98
112
  `go build -ldflags="-s -w" -o .bin/testFunc3 functions/func3`,
99
113
  {
100
114
  cwd: ".",
101
- env: { ...process.env, ...{ CGO_ENABLED: "0", GOOS: "linux" } },
115
+ env: {
116
+ ...process.env,
117
+ ...{
118
+ CGO_ENABLED: "0",
119
+ GOOS: "linux",
120
+ GOFLAGS: "-trimpath -buildvcs=false",
121
+ GOARCH: "amd64",
122
+ },
123
+ },
102
124
  },
103
125
  );
104
126
 
@@ -110,7 +132,15 @@ describe("Go Plugin", () => {
110
132
  `go build -ldflags="-s -w" -o .bin/testFunc5 functions/func5/main.go`,
111
133
  {
112
134
  cwd: ".",
113
- env: { ...process.env, ...{ CGO_ENABLED: "0", GOOS: "linux" } },
135
+ env: {
136
+ ...process.env,
137
+ ...{
138
+ CGO_ENABLED: "0",
139
+ GOOS: "linux",
140
+ GOFLAGS: "-trimpath -buildvcs=false",
141
+ GOARCH: "amd64",
142
+ },
143
+ },
114
144
  },
115
145
  );
116
146
 
@@ -122,7 +152,15 @@ describe("Go Plugin", () => {
122
152
  `go build -ldflags="-s -w" -o .bin/testFunc6 functions/func6`,
123
153
  {
124
154
  cwd: ".",
125
- env: { ...process.env, ...{ CGO_ENABLED: "0", GOOS: "linux" } },
155
+ env: {
156
+ ...process.env,
157
+ ...{
158
+ CGO_ENABLED: "0",
159
+ GOOS: "linux",
160
+ GOFLAGS: "-trimpath -buildvcs=false",
161
+ GOARCH: "amd64",
162
+ },
163
+ },
126
164
  },
127
165
  );
128
166
  });
@@ -155,7 +193,15 @@ describe("Go Plugin", () => {
155
193
  `go build -o .bin/testFunc1 functions/func1/main.go`,
156
194
  {
157
195
  cwd: ".",
158
- env: { ...process.env, ...{ CGO_ENABLED: "0", GOOS: "linux" } },
196
+ env: {
197
+ ...process.env,
198
+ ...{
199
+ CGO_ENABLED: "0",
200
+ GOOS: "linux",
201
+ GOFLAGS: "-trimpath -buildvcs=false",
202
+ GOARCH: "amd64",
203
+ },
204
+ },
159
205
  },
160
206
  );
161
207
  });
@@ -188,7 +234,12 @@ describe("Go Plugin", () => {
188
234
  cwd: ".",
189
235
  env: {
190
236
  ...process.env,
191
- ...{ CGO_ENABLED: "0", GOOS: "linux", GOARCH: "arm64" },
237
+ ...{
238
+ CGO_ENABLED: "0",
239
+ GOOS: "linux",
240
+ GOFLAGS: "-trimpath -buildvcs=false",
241
+ GOARCH: "arm64",
242
+ },
192
243
  },
193
244
  },
194
245
  );
@@ -222,7 +273,15 @@ describe("Go Plugin", () => {
222
273
  `go build -ldflags="-s -w" -o ../.bin/testFunc1 functions/func1/main.go`,
223
274
  {
224
275
  cwd: "gopath",
225
- env: { ...process.env, ...{ CGO_ENABLED: "0", GOOS: "linux" } },
276
+ env: {
277
+ ...process.env,
278
+ ...{
279
+ CGO_ENABLED: "0",
280
+ GOOS: "linux",
281
+ GOFLAGS: "-trimpath -buildvcs=false",
282
+ GOARCH: "amd64",
283
+ },
284
+ },
226
285
  },
227
286
  );
228
287
  });
@@ -323,7 +382,15 @@ describe("Go Plugin", () => {
323
382
  `go build -ldflags="-s -w" -o .bin/testFunc1 functions/func1/main.go`,
324
383
  {
325
384
  cwd: ".",
326
- env: { ...process.env, ...{ CGO_ENABLED: "0", GOOS: "linux" } },
385
+ env: {
386
+ ...process.env,
387
+ ...{
388
+ CGO_ENABLED: "0",
389
+ GOOS: "linux",
390
+ GOFLAGS: "-trimpath -buildvcs=false",
391
+ GOARCH: "amd64",
392
+ },
393
+ },
327
394
  },
328
395
  );
329
396
  });
@@ -365,7 +432,15 @@ describe("Go Plugin", () => {
365
432
  `go build -ldflags="-s -w" -o ../../.bin/testFunc1 .`,
366
433
  {
367
434
  cwd: "functions/func1",
368
- env: { ...process.env, ...{ CGO_ENABLED: "0", GOOS: "linux" } },
435
+ env: {
436
+ ...process.env,
437
+ ...{
438
+ CGO_ENABLED: "0",
439
+ GOOS: "linux",
440
+ GOFLAGS: "-trimpath -buildvcs=false",
441
+ GOARCH: "amd64",
442
+ },
443
+ },
369
444
  },
370
445
  );
371
446
 
@@ -377,7 +452,15 @@ describe("Go Plugin", () => {
377
452
  `go build -ldflags="-s -w" -o ../../.bin/testFunc2 main.go`,
378
453
  {
379
454
  cwd: "functions/func2",
380
- env: { ...process.env, ...{ CGO_ENABLED: "0", GOOS: "linux" } },
455
+ env: {
456
+ ...process.env,
457
+ ...{
458
+ CGO_ENABLED: "0",
459
+ GOOS: "linux",
460
+ GOFLAGS: "-trimpath -buildvcs=false",
461
+ GOARCH: "amd64",
462
+ },
463
+ },
381
464
  },
382
465
  );
383
466
  });
@@ -405,6 +488,19 @@ describe("Go Plugin", () => {
405
488
  );
406
489
  });
407
490
 
491
+ it("Merges custom env with default env", async () => {
492
+ config.service.setCustom({ go: { env: { FOO: "x" } } });
493
+
494
+ const plugin = new Go(config);
495
+
496
+ expect(plugin.config.env).to.deep.equal({
497
+ CGO_ENABLED: "0",
498
+ GOOS: "linux",
499
+ GOFLAGS: "-trimpath -buildvcs=false",
500
+ FOO: "x",
501
+ });
502
+ });
503
+
408
504
  it("Should get concurrency config", async () => {
409
505
  config.service.setFunctions({
410
506
  testFunc1: {
@@ -491,11 +587,25 @@ describe("Go Plugin", () => {
491
587
  // Verify beforeBuild commands were executed once
492
588
  expect(execStub).to.have.been.calledWith("echo 'before build command'", {
493
589
  cwd: ".",
494
- env: { ...process.env, ...{ CGO_ENABLED: "0", GOOS: "linux" } },
590
+ env: {
591
+ ...process.env,
592
+ ...{
593
+ CGO_ENABLED: "0",
594
+ GOOS: "linux",
595
+ GOFLAGS: "-trimpath -buildvcs=false",
596
+ },
597
+ },
495
598
  });
496
599
  expect(execStub).to.have.been.calledWith("go mod tidy", {
497
600
  cwd: ".",
498
- env: { ...process.env, ...{ CGO_ENABLED: "0", GOOS: "linux" } },
601
+ env: {
602
+ ...process.env,
603
+ ...{
604
+ CGO_ENABLED: "0",
605
+ GOOS: "linux",
606
+ GOFLAGS: "-trimpath -buildvcs=false",
607
+ },
608
+ },
499
609
  });
500
610
 
501
611
  // Verify compilation commands were executed for each function
@@ -503,14 +613,30 @@ describe("Go Plugin", () => {
503
613
  `go build -ldflags="-s -w" -o .bin/testFunc1 functions/func1/main.go`,
504
614
  {
505
615
  cwd: ".",
506
- env: { ...process.env, ...{ CGO_ENABLED: "0", GOOS: "linux" } },
616
+ env: {
617
+ ...process.env,
618
+ ...{
619
+ CGO_ENABLED: "0",
620
+ GOOS: "linux",
621
+ GOFLAGS: "-trimpath -buildvcs=false",
622
+ GOARCH: "amd64",
623
+ },
624
+ },
507
625
  },
508
626
  );
509
627
  expect(execStub).to.have.been.calledWith(
510
628
  `go build -ldflags="-s -w" -o .bin/testFunc2 functions/func2/main.go`,
511
629
  {
512
630
  cwd: ".",
513
- env: { ...process.env, ...{ CGO_ENABLED: "0", GOOS: "linux" } },
631
+ env: {
632
+ ...process.env,
633
+ ...{
634
+ CGO_ENABLED: "0",
635
+ GOOS: "linux",
636
+ GOFLAGS: "-trimpath -buildvcs=false",
637
+ GOARCH: "amd64",
638
+ },
639
+ },
514
640
  },
515
641
  );
516
642
 
@@ -545,11 +671,25 @@ describe("Go Plugin", () => {
545
671
  // Verify beforeBuild commands were executed once
546
672
  expect(execStub).to.have.been.calledWith("echo 'before build command'", {
547
673
  cwd: ".",
548
- env: { ...process.env, ...{ CGO_ENABLED: "0", GOOS: "linux" } },
674
+ env: {
675
+ ...process.env,
676
+ ...{
677
+ CGO_ENABLED: "0",
678
+ GOOS: "linux",
679
+ GOFLAGS: "-trimpath -buildvcs=false",
680
+ },
681
+ },
549
682
  });
550
683
  expect(execStub).to.have.been.calledWith("go mod tidy", {
551
684
  cwd: ".",
552
- env: { ...process.env, ...{ CGO_ENABLED: "0", GOOS: "linux" } },
685
+ env: {
686
+ ...process.env,
687
+ ...{
688
+ CGO_ENABLED: "0",
689
+ GOOS: "linux",
690
+ GOFLAGS: "-trimpath -buildvcs=false",
691
+ },
692
+ },
553
693
  });
554
694
 
555
695
  // Verify the compilation command was executed
@@ -557,7 +697,15 @@ describe("Go Plugin", () => {
557
697
  `go build -ldflags="-s -w" -o .bin/testFunc1 functions/func1/main.go`,
558
698
  {
559
699
  cwd: ".",
560
- env: { ...process.env, ...{ CGO_ENABLED: "0", GOOS: "linux" } },
700
+ env: {
701
+ ...process.env,
702
+ ...{
703
+ CGO_ENABLED: "0",
704
+ GOOS: "linux",
705
+ GOFLAGS: "-trimpath -buildvcs=false",
706
+ GOARCH: "amd64",
707
+ },
708
+ },
561
709
  },
562
710
  );
563
711
 
@@ -592,11 +740,25 @@ describe("Go Plugin", () => {
592
740
  // Verify beforeBuild commands were executed once
593
741
  expect(execStub).to.have.been.calledWith("echo 'before build command'", {
594
742
  cwd: ".",
595
- env: { ...process.env, ...{ CGO_ENABLED: "0", GOOS: "linux" } },
743
+ env: {
744
+ ...process.env,
745
+ ...{
746
+ CGO_ENABLED: "0",
747
+ GOOS: "linux",
748
+ GOFLAGS: "-trimpath -buildvcs=false",
749
+ },
750
+ },
596
751
  });
597
752
  expect(execStub).to.have.been.calledWith("go mod tidy", {
598
753
  cwd: ".",
599
- env: { ...process.env, ...{ CGO_ENABLED: "0", GOOS: "linux" } },
754
+ env: {
755
+ ...process.env,
756
+ ...{
757
+ CGO_ENABLED: "0",
758
+ GOOS: "linux",
759
+ GOFLAGS: "-trimpath -buildvcs=false",
760
+ },
761
+ },
600
762
  });
601
763
 
602
764
  // Verify compilation command was executed
@@ -604,11 +766,67 @@ describe("Go Plugin", () => {
604
766
  `go build -ldflags="-s -w" -o .bin/testFunc1 functions/func1/main.go`,
605
767
  {
606
768
  cwd: ".",
607
- env: { ...process.env, ...{ CGO_ENABLED: "0", GOOS: "linux" } },
769
+ env: {
770
+ ...process.env,
771
+ ...{
772
+ CGO_ENABLED: "0",
773
+ GOOS: "linux",
774
+ GOFLAGS: "-trimpath -buildvcs=false",
775
+ GOARCH: "amd64",
776
+ },
777
+ },
608
778
  },
609
779
  );
610
780
 
611
781
  // Total calls should be 3: 2 beforeBuild commands + 1 compilation command
612
782
  expect(execStub.callCount).to.equal(3);
613
783
  });
784
+ it("Does not leak arm64 GOARCH into x86_64 functions", async () => {
785
+ config.service.setFunctions({
786
+ armFunc: {
787
+ name: "armFunc",
788
+ runtime: "provided.al2023",
789
+ architecture: "arm64",
790
+ handler: "functions/arm",
791
+ },
792
+ x86Func: {
793
+ name: "x86Func",
794
+ runtime: "provided.al2023",
795
+ handler: "functions/x86",
796
+ },
797
+ });
798
+
799
+ const plugin = new Go(config);
800
+ plugin.exec = execStub;
801
+ plugin.readFileSync = readFileSyncStub;
802
+ plugin.zip = admZipStub;
803
+
804
+ await plugin.compileFunctions();
805
+
806
+ const envOf = (bin) =>
807
+ execStub.getCalls().find((c) => c.args[0].includes(bin)).args[1].env;
808
+ expect(envOf(".bin/armFunc").GOARCH).to.equal("arm64");
809
+ expect(envOf(".bin/x86Func").GOARCH).to.equal("amd64");
810
+ expect(plugin.config.env.GOARCH).to.equal(undefined);
811
+ });
812
+
813
+ it("Produces byte-identical zips for identical binaries", async () => {
814
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "sp-go-"));
815
+ const bin = path.join(dir, "fn");
816
+ fs.writeFileSync(bin, "fake binary content");
817
+ config.service.setFunctions({ fn: { name: "fn", handler: "fn" } });
818
+
819
+ const plugin = new Go(config);
820
+ plugin.zip = new AdmZip();
821
+
822
+ plugin.packageBootstrap("fn", bin);
823
+ const first = fs.readFileSync(bin + ".zip");
824
+ await new Promise((r) => setTimeout(r, 2100)); // DOS mtime has 2s resolution
825
+ plugin.zip = new AdmZip();
826
+ plugin.packageBootstrap("fn", bin);
827
+ const second = fs.readFileSync(bin + ".zip");
828
+
829
+ fs.rmSync(dir, { recursive: true });
830
+ expect(first.equals(second)).to.equal(true);
831
+ }).timeout(5000);
614
832
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "serverless-plugin-go",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "description": "Serverless Framework plugin that compiles Go functions on the fly",
6
6
  "main": "index.js",
@@ -54,7 +54,7 @@
54
54
  "eslint-plugin-import": "^2.29.0",
55
55
  "eslint-plugin-prettier": "^5.0.1",
56
56
  "fs": "^0.0.1-security",
57
- "mocha": "^10.2.0",
57
+ "mocha": "^12.0.3",
58
58
  "prettier": "^3.0.3",
59
59
  "sinon": "^17.0.0",
60
60
  "sinon-chai": "^3.7.0"