serverless-plugin-go 0.1.1 → 0.1.3
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 +1 -1
- package/CHANGELOG.md +18 -0
- package/README.md +2 -2
- package/index.js +14 -6
- package/index.test.js +162 -11
- package/package.json +2 -2
package/.cz.toml
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
## v0.1.3 (2026-11-10)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
- fix: per-function GOARCH and deterministic zip artifacts (#12)
|
|
5
|
+
- - Build env is now computed per function with an explicit GOARCH
|
|
6
|
+
(arm64/amd64). Previously config.env was mutated, so once an arm64
|
|
7
|
+
function compiled, every following x86_64 function was built as ARM,
|
|
8
|
+
and x86_64 builds on Apple Silicon used the host arch.
|
|
9
|
+
- Zip entries use a fixed mtime so identical binaries yield
|
|
10
|
+
byte-identical artifacts (stable hashes across builds).
|
|
11
|
+
- Bump mocha 10 -> 12 (mocha 10 fails to start on Node 26).
|
|
12
|
+
|
|
13
|
+
## v0.1.2 (2026-03-31)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
- chore: add support for provided.al2023 runtime (#11)
|
|
17
|
+
- Co-authored-by: Ariel Santos <ariel.santos@draftea.com>
|
|
18
|
+
|
|
1
19
|
## v0.1.1 (2026-01-21)
|
|
2
20
|
|
|
3
21
|
|
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
```yaml
|
|
32
32
|
functions:
|
|
33
33
|
example:
|
|
34
|
-
runtime: 'provided.al2
|
|
34
|
+
runtime: 'provided.al2023' # or provided.al2
|
|
35
35
|
handler: 'functions/example/main.go' # or just functions/example
|
|
36
36
|
```
|
|
37
37
|
|
|
@@ -87,7 +87,7 @@ Change architecture field from `provider` or function configuration to `arm64`:
|
|
|
87
87
|
```yaml
|
|
88
88
|
provider:
|
|
89
89
|
architecture: 'arm64'
|
|
90
|
-
runtime: 'provided.al2
|
|
90
|
+
runtime: 'provided.al2023' # or provided.al2
|
|
91
91
|
|
|
92
92
|
# If you define architecture at function level, this will have preference instead the provider configuration
|
|
93
93
|
|
package/index.js
CHANGED
|
@@ -8,10 +8,13 @@ import AdmZip from "adm-zip";
|
|
|
8
8
|
import { readFileSync } from "fs";
|
|
9
9
|
|
|
10
10
|
// SupportedRuntimes Runtimes that are allowed to run Golang lambdas.
|
|
11
|
-
const supportedRuntimes = ["provided.al2"];
|
|
11
|
+
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;
|
|
@@ -149,9 +152,12 @@ export default class Go {
|
|
|
149
152
|
return;
|
|
150
153
|
}
|
|
151
154
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
+
// Per-function env: never mutate shared config, and always set GOARCH so the
|
|
156
|
+
// host arch (e.g. Apple Silicon) never leaks into an x86_64 Lambda.
|
|
157
|
+
const env = {
|
|
158
|
+
...config.env,
|
|
159
|
+
GOARCH: arch === "arm64" ? "arm64" : "amd64",
|
|
160
|
+
};
|
|
155
161
|
|
|
156
162
|
const absHandler = path.resolve(config.baseDir);
|
|
157
163
|
const absBin = path.resolve(config.binDir);
|
|
@@ -172,7 +178,7 @@ export default class Go {
|
|
|
172
178
|
compileBinPath = path.relative(cwd, compileBinPath);
|
|
173
179
|
}
|
|
174
180
|
|
|
175
|
-
this.logDebug(`[${name}] env: ${JSON.stringify(
|
|
181
|
+
this.logDebug(`[${name}] env: ${JSON.stringify(env)}`);
|
|
176
182
|
this.logDebug(`[${name}] cwd: ${cwd}`);
|
|
177
183
|
this.logDebug(`[${name}] arch: ${arch}`);
|
|
178
184
|
|
|
@@ -181,7 +187,7 @@ export default class Go {
|
|
|
181
187
|
compileBinPath,
|
|
182
188
|
handler,
|
|
183
189
|
cwd,
|
|
184
|
-
|
|
190
|
+
env,
|
|
185
191
|
arch,
|
|
186
192
|
);
|
|
187
193
|
|
|
@@ -261,6 +267,8 @@ export default class Go {
|
|
|
261
267
|
*/
|
|
262
268
|
packageBootstrap(name, binPath) {
|
|
263
269
|
this.zip.addFile("bootstrap", this.readFileSync(binPath), "", 0o755);
|
|
270
|
+
// Fixed mtime so identical binaries produce byte-identical zips (stable hashes).
|
|
271
|
+
this.zip.getEntry("bootstrap").header.time = zipEpoch;
|
|
264
272
|
|
|
265
273
|
const zipPath = binPath + ".zip";
|
|
266
274
|
this.zip.writeZip(zipPath);
|
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
|
|
|
@@ -57,6 +63,16 @@ describe("Go Plugin", () => {
|
|
|
57
63
|
runtime: "go1.x",
|
|
58
64
|
handler: "functions/func4",
|
|
59
65
|
},
|
|
66
|
+
testFunc5: {
|
|
67
|
+
name: "testFunc5",
|
|
68
|
+
runtime: "provided.al2023",
|
|
69
|
+
handler: "functions/func5/main.go",
|
|
70
|
+
},
|
|
71
|
+
testFunc6: {
|
|
72
|
+
name: "testFunc6",
|
|
73
|
+
runtime: "provided.al2023",
|
|
74
|
+
handler: "functions/func6",
|
|
75
|
+
},
|
|
60
76
|
});
|
|
61
77
|
|
|
62
78
|
const plugin = new Go(config);
|
|
@@ -76,7 +92,10 @@ describe("Go Plugin", () => {
|
|
|
76
92
|
`go build -ldflags="-s -w" -o .bin/testFunc2 functions/func2/main.go`,
|
|
77
93
|
{
|
|
78
94
|
cwd: ".",
|
|
79
|
-
env: {
|
|
95
|
+
env: {
|
|
96
|
+
...process.env,
|
|
97
|
+
...{ CGO_ENABLED: "0", GOOS: "linux", GOARCH: "amd64" },
|
|
98
|
+
},
|
|
80
99
|
},
|
|
81
100
|
);
|
|
82
101
|
|
|
@@ -88,7 +107,40 @@ describe("Go Plugin", () => {
|
|
|
88
107
|
`go build -ldflags="-s -w" -o .bin/testFunc3 functions/func3`,
|
|
89
108
|
{
|
|
90
109
|
cwd: ".",
|
|
91
|
-
env: {
|
|
110
|
+
env: {
|
|
111
|
+
...process.env,
|
|
112
|
+
...{ CGO_ENABLED: "0", GOOS: "linux", GOARCH: "amd64" },
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
expect(config.service.functions.testFunc5.handler).to.equal(
|
|
118
|
+
`.bin/testFunc5`,
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
expect(execStub).to.have.been.calledWith(
|
|
122
|
+
`go build -ldflags="-s -w" -o .bin/testFunc5 functions/func5/main.go`,
|
|
123
|
+
{
|
|
124
|
+
cwd: ".",
|
|
125
|
+
env: {
|
|
126
|
+
...process.env,
|
|
127
|
+
...{ CGO_ENABLED: "0", GOOS: "linux", GOARCH: "amd64" },
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
expect(config.service.functions.testFunc6.handler).to.equal(
|
|
133
|
+
`.bin/testFunc6`,
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
expect(execStub).to.have.been.calledWith(
|
|
137
|
+
`go build -ldflags="-s -w" -o .bin/testFunc6 functions/func6`,
|
|
138
|
+
{
|
|
139
|
+
cwd: ".",
|
|
140
|
+
env: {
|
|
141
|
+
...process.env,
|
|
142
|
+
...{ CGO_ENABLED: "0", GOOS: "linux", GOARCH: "amd64" },
|
|
143
|
+
},
|
|
92
144
|
},
|
|
93
145
|
);
|
|
94
146
|
});
|
|
@@ -121,7 +173,10 @@ describe("Go Plugin", () => {
|
|
|
121
173
|
`go build -o .bin/testFunc1 functions/func1/main.go`,
|
|
122
174
|
{
|
|
123
175
|
cwd: ".",
|
|
124
|
-
env: {
|
|
176
|
+
env: {
|
|
177
|
+
...process.env,
|
|
178
|
+
...{ CGO_ENABLED: "0", GOOS: "linux", GOARCH: "amd64" },
|
|
179
|
+
},
|
|
125
180
|
},
|
|
126
181
|
);
|
|
127
182
|
});
|
|
@@ -188,7 +243,10 @@ describe("Go Plugin", () => {
|
|
|
188
243
|
`go build -ldflags="-s -w" -o ../.bin/testFunc1 functions/func1/main.go`,
|
|
189
244
|
{
|
|
190
245
|
cwd: "gopath",
|
|
191
|
-
env: {
|
|
246
|
+
env: {
|
|
247
|
+
...process.env,
|
|
248
|
+
...{ CGO_ENABLED: "0", GOOS: "linux", GOARCH: "amd64" },
|
|
249
|
+
},
|
|
192
250
|
},
|
|
193
251
|
);
|
|
194
252
|
});
|
|
@@ -243,6 +301,30 @@ describe("Go Plugin", () => {
|
|
|
243
301
|
expect(execStub).to.have.been.calledOnce;
|
|
244
302
|
});
|
|
245
303
|
|
|
304
|
+
it("Compiles Go function with global runtime provided.al2023 defined", async () => {
|
|
305
|
+
config.service
|
|
306
|
+
.setProvider({
|
|
307
|
+
runtime: "provided.al2023",
|
|
308
|
+
})
|
|
309
|
+
.setFunctions({
|
|
310
|
+
testFunc1: {
|
|
311
|
+
name: "testFunc1",
|
|
312
|
+
handler: "functions/func1/main.go",
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
const plugin = new Go(config);
|
|
317
|
+
plugin.exec = execStub;
|
|
318
|
+
plugin.readFileSync = readFileSyncStub;
|
|
319
|
+
plugin.zip = admZipStub;
|
|
320
|
+
|
|
321
|
+
// when
|
|
322
|
+
await plugin.hooks["before:package:createDeploymentArtifacts"]();
|
|
323
|
+
|
|
324
|
+
// then
|
|
325
|
+
expect(execStub).to.have.been.calledOnce;
|
|
326
|
+
});
|
|
327
|
+
|
|
246
328
|
it("Compiles single Go function", async () => {
|
|
247
329
|
config.service.setFunctions({
|
|
248
330
|
testFunc1: {
|
|
@@ -265,7 +347,10 @@ describe("Go Plugin", () => {
|
|
|
265
347
|
`go build -ldflags="-s -w" -o .bin/testFunc1 functions/func1/main.go`,
|
|
266
348
|
{
|
|
267
349
|
cwd: ".",
|
|
268
|
-
env: {
|
|
350
|
+
env: {
|
|
351
|
+
...process.env,
|
|
352
|
+
...{ CGO_ENABLED: "0", GOOS: "linux", GOARCH: "amd64" },
|
|
353
|
+
},
|
|
269
354
|
},
|
|
270
355
|
);
|
|
271
356
|
});
|
|
@@ -307,7 +392,10 @@ describe("Go Plugin", () => {
|
|
|
307
392
|
`go build -ldflags="-s -w" -o ../../.bin/testFunc1 .`,
|
|
308
393
|
{
|
|
309
394
|
cwd: "functions/func1",
|
|
310
|
-
env: {
|
|
395
|
+
env: {
|
|
396
|
+
...process.env,
|
|
397
|
+
...{ CGO_ENABLED: "0", GOOS: "linux", GOARCH: "amd64" },
|
|
398
|
+
},
|
|
311
399
|
},
|
|
312
400
|
);
|
|
313
401
|
|
|
@@ -319,7 +407,10 @@ describe("Go Plugin", () => {
|
|
|
319
407
|
`go build -ldflags="-s -w" -o ../../.bin/testFunc2 main.go`,
|
|
320
408
|
{
|
|
321
409
|
cwd: "functions/func2",
|
|
322
|
-
env: {
|
|
410
|
+
env: {
|
|
411
|
+
...process.env,
|
|
412
|
+
...{ CGO_ENABLED: "0", GOOS: "linux", GOARCH: "amd64" },
|
|
413
|
+
},
|
|
323
414
|
},
|
|
324
415
|
);
|
|
325
416
|
});
|
|
@@ -445,14 +536,20 @@ describe("Go Plugin", () => {
|
|
|
445
536
|
`go build -ldflags="-s -w" -o .bin/testFunc1 functions/func1/main.go`,
|
|
446
537
|
{
|
|
447
538
|
cwd: ".",
|
|
448
|
-
env: {
|
|
539
|
+
env: {
|
|
540
|
+
...process.env,
|
|
541
|
+
...{ CGO_ENABLED: "0", GOOS: "linux", GOARCH: "amd64" },
|
|
542
|
+
},
|
|
449
543
|
},
|
|
450
544
|
);
|
|
451
545
|
expect(execStub).to.have.been.calledWith(
|
|
452
546
|
`go build -ldflags="-s -w" -o .bin/testFunc2 functions/func2/main.go`,
|
|
453
547
|
{
|
|
454
548
|
cwd: ".",
|
|
455
|
-
env: {
|
|
549
|
+
env: {
|
|
550
|
+
...process.env,
|
|
551
|
+
...{ CGO_ENABLED: "0", GOOS: "linux", GOARCH: "amd64" },
|
|
552
|
+
},
|
|
456
553
|
},
|
|
457
554
|
);
|
|
458
555
|
|
|
@@ -499,7 +596,10 @@ describe("Go Plugin", () => {
|
|
|
499
596
|
`go build -ldflags="-s -w" -o .bin/testFunc1 functions/func1/main.go`,
|
|
500
597
|
{
|
|
501
598
|
cwd: ".",
|
|
502
|
-
env: {
|
|
599
|
+
env: {
|
|
600
|
+
...process.env,
|
|
601
|
+
...{ CGO_ENABLED: "0", GOOS: "linux", GOARCH: "amd64" },
|
|
602
|
+
},
|
|
503
603
|
},
|
|
504
604
|
);
|
|
505
605
|
|
|
@@ -546,11 +646,62 @@ describe("Go Plugin", () => {
|
|
|
546
646
|
`go build -ldflags="-s -w" -o .bin/testFunc1 functions/func1/main.go`,
|
|
547
647
|
{
|
|
548
648
|
cwd: ".",
|
|
549
|
-
env: {
|
|
649
|
+
env: {
|
|
650
|
+
...process.env,
|
|
651
|
+
...{ CGO_ENABLED: "0", GOOS: "linux", GOARCH: "amd64" },
|
|
652
|
+
},
|
|
550
653
|
},
|
|
551
654
|
);
|
|
552
655
|
|
|
553
656
|
// Total calls should be 3: 2 beforeBuild commands + 1 compilation command
|
|
554
657
|
expect(execStub.callCount).to.equal(3);
|
|
555
658
|
});
|
|
659
|
+
it("Does not leak arm64 GOARCH into x86_64 functions", async () => {
|
|
660
|
+
config.service.setFunctions({
|
|
661
|
+
armFunc: {
|
|
662
|
+
name: "armFunc",
|
|
663
|
+
runtime: "provided.al2023",
|
|
664
|
+
architecture: "arm64",
|
|
665
|
+
handler: "functions/arm",
|
|
666
|
+
},
|
|
667
|
+
x86Func: {
|
|
668
|
+
name: "x86Func",
|
|
669
|
+
runtime: "provided.al2023",
|
|
670
|
+
handler: "functions/x86",
|
|
671
|
+
},
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
const plugin = new Go(config);
|
|
675
|
+
plugin.exec = execStub;
|
|
676
|
+
plugin.readFileSync = readFileSyncStub;
|
|
677
|
+
plugin.zip = admZipStub;
|
|
678
|
+
|
|
679
|
+
await plugin.compileFunctions();
|
|
680
|
+
|
|
681
|
+
const envOf = (bin) =>
|
|
682
|
+
execStub.getCalls().find((c) => c.args[0].includes(bin)).args[1].env;
|
|
683
|
+
expect(envOf(".bin/armFunc").GOARCH).to.equal("arm64");
|
|
684
|
+
expect(envOf(".bin/x86Func").GOARCH).to.equal("amd64");
|
|
685
|
+
expect(plugin.config.env.GOARCH).to.equal(undefined);
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
it("Produces byte-identical zips for identical binaries", async () => {
|
|
689
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "sp-go-"));
|
|
690
|
+
const bin = path.join(dir, "fn");
|
|
691
|
+
fs.writeFileSync(bin, "fake binary content");
|
|
692
|
+
config.service.setFunctions({ fn: { name: "fn", handler: "fn" } });
|
|
693
|
+
|
|
694
|
+
const plugin = new Go(config);
|
|
695
|
+
plugin.zip = new AdmZip();
|
|
696
|
+
|
|
697
|
+
plugin.packageBootstrap("fn", bin);
|
|
698
|
+
const first = fs.readFileSync(bin + ".zip");
|
|
699
|
+
await new Promise((r) => setTimeout(r, 2100)); // DOS mtime has 2s resolution
|
|
700
|
+
plugin.zip = new AdmZip();
|
|
701
|
+
plugin.packageBootstrap("fn", bin);
|
|
702
|
+
const second = fs.readFileSync(bin + ".zip");
|
|
703
|
+
|
|
704
|
+
fs.rmSync(dir, { recursive: true });
|
|
705
|
+
expect(first.equals(second)).to.equal(true);
|
|
706
|
+
}).timeout(5000);
|
|
556
707
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "serverless-plugin-go",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
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": "^
|
|
57
|
+
"mocha": "^12.0.3",
|
|
58
58
|
"prettier": "^3.0.3",
|
|
59
59
|
"sinon": "^17.0.0",
|
|
60
60
|
"sinon-chai": "^3.7.0"
|