Skip to content

Commit e66cecf

Browse files
authored
bug: handle suffixes and commas in availableMemoryMb (#335)
* bug: handle suffixes and commas in availableMemoryMb * add more test coverage
1 parent 9a1c277 commit e66cecf

File tree

7 files changed

+93
-12
lines changed

7 files changed

+93
-12
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ jobs:
7575

7676
- `entry_point`: (Optional) Name of a function (as defined in source code) that will be executed. Defaults to the resource name suffix, if not specified.
7777

78-
- `memory_mb`: (Optional) The amount of memory in MB available for a function. Defaults to 256MB.
78+
- `memory_mb`: (Optional) The amount of memory in MB available for a function. Defaults to '256' (256 MB). The value must be the number of megabytes _without_ the unit suffix.
7979

8080
- `region`: (Optional) [Region](https://cloud.google.com/functions/docs/locations) in which the function should be deployed. Defaults to `us-central1`.
8181

action.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ inputs:
8080

8181
memory_mb:
8282
description: |-
83-
The amount of memory in MB available for a function. Defaults to 256MB.
83+
The amount of memory in MB available for a function. Defaults to 256.
8484
required: false
8585

8686
vpc_connector:

src/main.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import {
3939

4040
import { CloudFunction, CloudFunctionsClient, SecretEnvVar, SecretVolume } from './client';
4141
import { SecretName } from './secret';
42-
import { formatEntry, toEnum } from './util';
42+
import { formatEntry, stringToInt, toEnum } from './util';
4343

4444
async function run(): Promise<void> {
4545
try {
@@ -49,7 +49,7 @@ async function run(): Promise<void> {
4949
const description = presence(getInput('description'));
5050
const credentials = presence(getInput('credentials'));
5151
let projectID = presence(getInput('project_id'));
52-
const availableMemoryMb = presence(getInput('memory_mb'));
52+
const availableMemoryMb = stringToInt(getInput('memory_mb'));
5353
const region = presence(getInput('region') || 'us-central1');
5454
const envVars = presence(getInput('env_vars'));
5555
const envVarsFile = presence(getInput('env_vars_file'));
@@ -189,7 +189,7 @@ async function run(): Promise<void> {
189189
name: name,
190190
runtime: runtime,
191191
description: description,
192-
availableMemoryMb: availableMemoryMb ? +availableMemoryMb : undefined,
192+
availableMemoryMb: availableMemoryMb,
193193
buildEnvironmentVariables: buildEnvironmentVariables,
194194
buildWorkerPool: buildWorkerPool,
195195
dockerRegistry: dockerRegistry,

src/util.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,25 @@ export function formatEntry(entry: RealEntryData): string {
118118
export function toEnum(s: string): string {
119119
return (s || '').replace(/[\s-]+/g, '_').toUpperCase();
120120
}
121+
122+
/**
123+
* stringToInt is a helper that converts the given string into an integer. If
124+
* the given string is empty, it returns undefined. If the string is not empty
125+
* and parseInt fails (returns NaN), it throws an error. Otherwise, it returns
126+
* the integer value.
127+
*
128+
* @param str String to parse as an int.
129+
* @returns Parsed integer or undefined if the input was the empty string.
130+
*/
131+
export function stringToInt(str: string): number | undefined {
132+
str = (str || '').trim().replace(/[_,]/g, '');
133+
if (str === '') {
134+
return undefined;
135+
}
136+
137+
const result = parseInt(str);
138+
if (isNaN(result)) {
139+
throw new Error(`input "${str}" is not a number`);
140+
}
141+
return result;
142+
}

tests/client.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -314,9 +314,9 @@ describe('CloudFunctionsClient', () => {
314314

315315
cases.forEach((tc) => {
316316
it(tc.name, async () => {
317-
if (tc.expected) {
317+
if (tc.expected !== undefined) {
318318
expect(tc.client.fullResourceName(tc.input)).to.eql(tc.expected);
319-
} else if (tc.error) {
319+
} else if (tc.error !== undefined) {
320320
expect(() => {
321321
tc.client.fullResourceName(tc.input);
322322
}).to.throw(tc.error);

tests/secret.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,12 @@ describe('SecretName', function () {
8888

8989
cases.forEach((tc) => {
9090
it(tc.name, async () => {
91-
if (tc.expected) {
91+
if (tc.expected !== undefined) {
9292
const secret = new SecretName(tc.input);
9393
expect(secret.project).to.eq(tc.expected.project);
9494
expect(secret.name).to.eq(tc.expected.secret);
9595
expect(secret.version).to.eq(tc.expected.version);
96-
} else if (tc.error) {
96+
} else if (tc.error !== undefined) {
9797
expect(() => {
9898
new SecretName(tc.input);
9999
}).to.throw(tc.error);

tests/util.test.ts

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { expect } from 'chai';
66
import StreamZip from 'node-stream-zip';
77
import { randomFilepath } from '@google-github-actions/actions-utils';
88

9-
import { toEnum, zipDir } from '../src/util';
9+
import { stringToInt, toEnum, zipDir } from '../src/util';
1010

1111
describe('Util', () => {
1212
describe('#zipDir', () => {
@@ -44,11 +44,11 @@ describe('Util', () => {
4444

4545
cases.forEach((tc) => {
4646
it(tc.name, async () => {
47-
if (tc.expectedFiles) {
47+
if (tc.expectedFiles !== undefined) {
4848
const zf = await zipDir(tc.zipDir, randomFilepath());
4949
const filesInsideZip = await getFilesInZip(zf);
5050
expect(filesInsideZip).to.have.members(tc.expectedFiles);
51-
} else if (tc.error) {
51+
} else if (tc.error !== undefined) {
5252
try {
5353
await zipDir(tc.zipDir, randomFilepath());
5454
throw new Error(`Should have thrown err: ${tc.error}`);
@@ -105,6 +105,65 @@ describe('Util', () => {
105105
});
106106
});
107107
});
108+
109+
describe('#stringToInt', () => {
110+
const cases: {
111+
only?: boolean;
112+
name: string;
113+
input: string;
114+
expected?: number | undefined;
115+
error?: string;
116+
}[] = [
117+
{
118+
name: 'empty',
119+
input: '',
120+
expected: undefined,
121+
},
122+
{
123+
name: 'spaces',
124+
input: ' ',
125+
expected: undefined,
126+
},
127+
{
128+
name: 'digit',
129+
input: '1',
130+
expected: 1,
131+
},
132+
{
133+
name: 'multi-digit',
134+
input: '123',
135+
expected: 123,
136+
},
137+
{
138+
name: 'suffix',
139+
input: '100MB',
140+
expected: 100,
141+
},
142+
{
143+
name: 'comma',
144+
input: '1,000',
145+
expected: 1000,
146+
},
147+
{
148+
name: 'NaN',
149+
input: 'this is definitely not a number',
150+
error: 'input "this is definitely not a number" is not a number',
151+
},
152+
];
153+
154+
cases.forEach((tc) => {
155+
const fn = tc.only ? it.only : it;
156+
fn(tc.name, () => {
157+
if (tc.expected !== undefined) {
158+
expect(stringToInt(tc.input)).to.eql(tc.expected);
159+
} else if (tc.error !== undefined) {
160+
expect(() => {
161+
stringToInt(tc.input);
162+
}).to.throw(tc.error);
163+
}
164+
});
165+
});
166+
});
108167
});
109168

110169
/**

0 commit comments

Comments
 (0)