Remove allowServiceContainerCommand feature flag (#345)
Service container entrypoint/command support is now unconditional.
This commit is contained in:
@@ -0,0 +1,33 @@
|
|||||||
|
# Agents
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
```
|
||||||
|
npx lerna run build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test
|
||||||
|
|
||||||
|
```
|
||||||
|
npm -w @actions/expressions test
|
||||||
|
npm -w @actions/workflow-parser test
|
||||||
|
npm -w @actions/languageservice test
|
||||||
|
```
|
||||||
|
|
||||||
|
## Format
|
||||||
|
|
||||||
|
Always run formatting before committing:
|
||||||
|
|
||||||
|
```
|
||||||
|
npx prettier --write <changed files>
|
||||||
|
```
|
||||||
|
|
||||||
|
Verify with:
|
||||||
|
|
||||||
|
```
|
||||||
|
npm run format-check -ws
|
||||||
|
```
|
||||||
|
|
||||||
|
## Feature flags
|
||||||
|
|
||||||
|
Feature flags are defined in `expressions/src/features.ts` (`ExperimentalFeatures` interface + `allFeatureKeys` array). They are plumbed through `ConvertOptions`, `CompletionConfig`, `ValidationConfig`, and `initializationOptions`. When a feature graduates to stable, remove its flag and make the behavior unconditional.
|
||||||
@@ -55,8 +55,7 @@ describe("FeatureFlags", () => {
|
|||||||
"missingInputsQuickfix",
|
"missingInputsQuickfix",
|
||||||
"blockScalarChompingWarning",
|
"blockScalarChompingWarning",
|
||||||
"allowCaseFunction",
|
"allowCaseFunction",
|
||||||
"allowCopilotRequestsPermission",
|
"allowCopilotRequestsPermission"
|
||||||
"allowServiceContainerCommand"
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -40,12 +40,6 @@ export interface ExperimentalFeatures {
|
|||||||
* @default false
|
* @default false
|
||||||
*/
|
*/
|
||||||
allowCopilotRequestsPermission?: boolean;
|
allowCopilotRequestsPermission?: boolean;
|
||||||
|
|
||||||
/**
|
|
||||||
* Enable `entrypoint` and `command` keys in service containers (`jobs.<job_id>.services.*`).
|
|
||||||
* @default false
|
|
||||||
*/
|
|
||||||
allowServiceContainerCommand?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -61,8 +55,7 @@ const allFeatureKeys: ExperimentalFeatureKey[] = [
|
|||||||
"missingInputsQuickfix",
|
"missingInputsQuickfix",
|
||||||
"blockScalarChompingWarning",
|
"blockScalarChompingWarning",
|
||||||
"allowCaseFunction",
|
"allowCaseFunction",
|
||||||
"allowCopilotRequestsPermission",
|
"allowCopilotRequestsPermission"
|
||||||
"allowServiceContainerCommand"
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export class FeatureFlags {
|
export class FeatureFlags {
|
||||||
|
|||||||
@@ -1,24 +1,18 @@
|
|||||||
import {FeatureFlags} from "@actions/expressions";
|
|
||||||
import {registerLogger} from "./log.js";
|
import {registerLogger} from "./log.js";
|
||||||
import {createDocument} from "./test-utils/document.js";
|
import {createDocument} from "./test-utils/document.js";
|
||||||
import {TestLogger} from "./test-utils/logger.js";
|
import {TestLogger} from "./test-utils/logger.js";
|
||||||
import {clearCache} from "./utils/workflow-cache.js";
|
import {clearCache} from "./utils/workflow-cache.js";
|
||||||
import {validate, ValidationConfig} from "./validate.js";
|
import {validate} from "./validate.js";
|
||||||
|
|
||||||
registerLogger(new TestLogger());
|
registerLogger(new TestLogger());
|
||||||
|
|
||||||
const configWithFlag: ValidationConfig = {
|
|
||||||
featureFlags: new FeatureFlags({allowServiceContainerCommand: true})
|
|
||||||
};
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
clearCache();
|
clearCache();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("service container command/entrypoint", () => {
|
describe("service container command/entrypoint", () => {
|
||||||
describe("with feature flag enabled", () => {
|
it("allows command in service container", async () => {
|
||||||
it("allows command in service container", async () => {
|
const input = `
|
||||||
const input = `
|
|
||||||
on: push
|
on: push
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -30,13 +24,13 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- run: echo hi
|
- run: echo hi
|
||||||
`;
|
`;
|
||||||
const result = await validate(createDocument("wf.yaml", input), configWithFlag);
|
const result = await validate(createDocument("wf.yaml", input));
|
||||||
const commandErrors = result.filter(d => d.message.includes("command"));
|
const commandErrors = result.filter(d => d.message.includes("command"));
|
||||||
expect(commandErrors).toEqual([]);
|
expect(commandErrors).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("allows entrypoint in service container", async () => {
|
it("allows entrypoint in service container", async () => {
|
||||||
const input = `
|
const input = `
|
||||||
on: push
|
on: push
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -48,13 +42,13 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- run: echo hi
|
- run: echo hi
|
||||||
`;
|
`;
|
||||||
const result = await validate(createDocument("wf.yaml", input), configWithFlag);
|
const result = await validate(createDocument("wf.yaml", input));
|
||||||
const entrypointErrors = result.filter(d => d.message.includes("entrypoint"));
|
const entrypointErrors = result.filter(d => d.message.includes("entrypoint"));
|
||||||
expect(entrypointErrors).toEqual([]);
|
expect(entrypointErrors).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("allows both command and entrypoint in service container", async () => {
|
it("allows both command and entrypoint in service container", async () => {
|
||||||
const input = `
|
const input = `
|
||||||
on: push
|
on: push
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -67,13 +61,13 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- run: echo hi
|
- run: echo hi
|
||||||
`;
|
`;
|
||||||
const result = await validate(createDocument("wf.yaml", input), configWithFlag);
|
const result = await validate(createDocument("wf.yaml", input));
|
||||||
const relevantErrors = result.filter(d => d.message.includes("command") || d.message.includes("entrypoint"));
|
const relevantErrors = result.filter(d => d.message.includes("command") || d.message.includes("entrypoint"));
|
||||||
expect(relevantErrors).toEqual([]);
|
expect(relevantErrors).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("rejects command in job container even with flag enabled", async () => {
|
it("rejects command in job container", async () => {
|
||||||
const input = `
|
const input = `
|
||||||
on: push
|
on: push
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -84,13 +78,13 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- run: echo hi
|
- run: echo hi
|
||||||
`;
|
`;
|
||||||
const result = await validate(createDocument("wf.yaml", input), configWithFlag);
|
const result = await validate(createDocument("wf.yaml", input));
|
||||||
const commandErrors = result.filter(d => d.message.includes("command"));
|
const commandErrors = result.filter(d => d.message.includes("command"));
|
||||||
expect(commandErrors.length).toBeGreaterThan(0);
|
expect(commandErrors.length).toBeGreaterThan(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("rejects entrypoint in job container even with flag enabled", async () => {
|
it("rejects entrypoint in job container", async () => {
|
||||||
const input = `
|
const input = `
|
||||||
on: push
|
on: push
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -101,47 +95,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- run: echo hi
|
- run: echo hi
|
||||||
`;
|
`;
|
||||||
const result = await validate(createDocument("wf.yaml", input), configWithFlag);
|
const result = await validate(createDocument("wf.yaml", input));
|
||||||
const entrypointErrors = result.filter(d => d.message.includes("entrypoint"));
|
const entrypointErrors = result.filter(d => d.message.includes("entrypoint"));
|
||||||
expect(entrypointErrors.length).toBeGreaterThan(0);
|
expect(entrypointErrors.length).toBeGreaterThan(0);
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("with feature flag disabled", () => {
|
|
||||||
it("rejects command in service container", async () => {
|
|
||||||
const input = `
|
|
||||||
on: push
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
services:
|
|
||||||
redis:
|
|
||||||
image: redis
|
|
||||||
command: --port 6380
|
|
||||||
steps:
|
|
||||||
- run: echo hi
|
|
||||||
`;
|
|
||||||
const result = await validate(createDocument("wf.yaml", input));
|
|
||||||
const commandErrors = result.filter(d => d.message.includes("command"));
|
|
||||||
expect(commandErrors.length).toBeGreaterThan(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("rejects entrypoint in service container", async () => {
|
|
||||||
const input = `
|
|
||||||
on: push
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
services:
|
|
||||||
redis:
|
|
||||||
image: redis
|
|
||||||
entrypoint: /usr/local/bin/redis-server
|
|
||||||
steps:
|
|
||||||
- run: echo hi
|
|
||||||
`;
|
|
||||||
const result = await validate(createDocument("wf.yaml", input));
|
|
||||||
const entrypointErrors = result.filter(d => d.message.includes("entrypoint"));
|
|
||||||
expect(entrypointErrors.length).toBeGreaterThan(0);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -146,15 +146,11 @@ export function convertToServiceContainer(context: TemplateContext, container: T
|
|||||||
|
|
||||||
export function convertToJobServices(context: TemplateContext, services: TemplateToken): Container[] | undefined {
|
export function convertToJobServices(context: TemplateContext, services: TemplateToken): Container[] | undefined {
|
||||||
const serviceList: Container[] = [];
|
const serviceList: Container[] = [];
|
||||||
const flags = context.state.featureFlags as import("@actions/expressions/features").FeatureFlags | undefined;
|
|
||||||
const useServiceContainer = flags?.isEnabled("allowServiceContainerCommand") ?? false;
|
|
||||||
|
|
||||||
const mapping = services.assertMapping("services");
|
const mapping = services.assertMapping("services");
|
||||||
for (const service of mapping) {
|
for (const service of mapping) {
|
||||||
service.key.assertString("service key");
|
service.key.assertString("service key");
|
||||||
const container = useServiceContainer
|
const container = convertToServiceContainer(context, service.value);
|
||||||
? convertToServiceContainer(context, service.value)
|
|
||||||
: convertToJobContainer(context, service.value);
|
|
||||||
if (container) {
|
if (container) {
|
||||||
serviceList.push(container);
|
serviceList.push(container);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user