Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions .github/workflows/updateCodeJSON.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
name: Update Code.json
on:
workflow_dispatch:
pull_request:
types: [opened, synchronize]
paths:
- "code.json"

permissions:
contents: write
Expand All @@ -18,12 +22,12 @@ jobs:

- name: Update code.json
id: generator
uses: DSACMS/automated-codejson-generator@main
uses: DSACMS/automated-codejson-generator@sachin/jsonValidationImplementation
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ADMIN_TOKEN: ${{ secrets.ADMIN_PAT }}
ADMIN_TOKEN: ${{ secrets.ADMIN_PAT }}
BRANCH: "main"
SKIP_PR: "true"
SKIP_PR: "false"

- name: Post update information
run: |
Expand All @@ -33,4 +37,3 @@ jobs:
elif [ "${{ steps.generator.outputs.method_used }}" = "pull_request" ]; then
echo "Created pull request: ${{ steps.generator.outputs.pr_url }}"
fi

12 changes: 12 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,18 @@ npm run package
npm test
```

## Validation

The action uses [Zod](https://zod.dev/) for schema validation, automatically validating code.json in two scenarios:

### 1. Before Generation

Every time the action generates or updates code.json (via schedule or workflow_dispatch), it validates the output before creating a PR or pushing. If validation fails, no changes are made.

### 2. On PR Edits

When the `pull_request` trigger is configured, the action validates code.json whenever it's edited in a PR. This ensures users cannot accidentally merge invalid JSON.

### Workflow and Branching

We follow the [GitHub Flow Workflow](https://guides.github.com/introduction/flow/):
Expand Down
55 changes: 41 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,13 @@ ADMIN_TOKEN:
```yaml
updated:
description: "Boolean indicating whether code.json was updated"

pr_url:
description: "URL of the created pull request if changes were made via PR"

commit_sha:
description: "SHA of the commit if pushed directly to branch"

method_used:
description: "Method used for the update: 'direct_push' or 'pull_request'"
```
Expand All @@ -45,14 +48,18 @@ method_used:

### Option 1: Direct Push

This approach tries to push directly to the branch using a Personal Access Token, but falls back to creating a pull request if the direct push fails.
This approach tries to push directly to the branch using a Personal Access Token, but falls back to creating a pull request if the direct push fails. When users need to edit code.json, they should create a PR which will automatically validate their changes.

```yaml
name: Update Code.json (Smart Mode)
name: Update Code.json
on:
schedule:
- cron: 0 0 1 * * # First day of every month
workflow_dispatch:
pull_request:
types: [opened, synchronize]
paths:
- "code.json"

permissions:
contents: write
Expand All @@ -73,7 +80,7 @@ jobs:
uses: DSACMS/automated-codejson-generator@v1.2.0
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ADMIN_TOKEN: ${{ secrets.ADMIN_PAT }} # PAT with admin/push permissions
ADMIN_TOKEN: ${{ secrets.ADMIN_PAT }} # PAT with admin/push permissions
BRANCH: "main"
SKIP_PR: "true"

Expand All @@ -89,14 +96,18 @@ jobs:

### Option 2: Pull Request Only

This approach always creates a pull request, ensuring code review for all changes.
This approach always creates a pull request for both automatic generation and validation of manual edits, ensuring code review for all changes.

```yaml
name: Update Code.json
on:
schedule:
- cron: 0 0 1 * * # First day of every month
workflow_dispatch:
pull_request:
types: [opened, synchronize]
paths:
- "code.json"

permissions:
contents: write
Expand All @@ -113,13 +124,29 @@ jobs:
fetch-depth: 0

- name: Update code.json
uses: DSACMS/automated-codejson-generator@latest
uses: DSACMS/automated-codejson-generator@v1.2.0
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRANCH: "main"
SKIP_PR: "false"
SKIP_PR: "false"
```

### How It Works

**Automatic Generation**

- The action calculates metadata, validates it, and creates a PR or pushes directly
- Validation ensures only valid code.json is created
- Users can then fill in manual fields by editing the PR

**PR Validation**

- When users edit code.json in a PR, validation runs automatically on every commit
- The PR cannot be merged if validation fails (when branch protection is enabled)
- Error messages help users fix issues quickly

**Important:** For direct push mode, users should always create PRs when manually editing code.json to ensure validation runs. Direct edits to the main branch will not be validated by this action.

Comment thread
sachin-panayil marked this conversation as resolved.
## Setting Up Personal Access Token (PAT)

To use the direct push functionality, you'll need to create a Personal Access Token:
Expand All @@ -128,14 +155,14 @@ To use the direct push functionality, you'll need to create a Personal Access To

1. **Go to GitHub Settings**: Navigate to your GitHub account settings
2. **Developer Settings**: Click on "Developer settings" in the left sidebar
3. **Personal Access Tokens**: Choose "Tokens (classic)" or "Fine-grained tokens"
3. **Personal Access Tokens**: Choose "Tokens (classic)"
4. **Generate New Token**: Click "Generate new token"
5. **Configure Token**:
- **Name**: Give it a descriptive name like "Code.json Generator"
- **Name**: Give it a name like "code.json Generator"
- **Expiration**: Set appropriate expiration (recommend 90 days or 1 year)
- **Scopes**:
- For classic tokens: Select `repo` (full repository access)
- For fine-grained tokens: Select `Contents` (write) and `Metadata` (read)
- **Scopes**:
- Select `repo` (full repository access)
6. **Store Token**: Copy and paste your token and store it for the next part

### Adding PAT to Repository

Expand All @@ -148,8 +175,7 @@ To use the direct push functionality, you'll need to create a Personal Access To
5. **Save**: Click "Add secret"

⚠️ _Please make sure the following are enabled within your Repository Action Settings in order to work properly_ ⚠️
<img width="789" height="361" alt="Screenshot 2025-08-05 at 1 44 36 PM" src="https://github.com/user-attachments/assets/3795dc0e-c4c4-4378-8eb2-b7b9d861c08a" />

<img width="789" height="361" alt="Screenshot 2025-08-05 at 1 44 36 PM" src="https://github.com/user-attachments/assets/3795dc0e-c4c4-4378-8eb2-b7b9d861c08a" />

## Generation Context

Expand All @@ -173,7 +199,7 @@ The automated code.json generator calculates specific fields by analyzing your r

**dateLastModified**: This uses your repository's last update timestamp, reflecting the most recent changes. No configuration needed.

**dateMetaDataLastUpdated**: The generator sets this to the current timestamp each time it runs, providing a record of when the metadata was last refreshed. No configuration needed.
**dateMetadataLastUpdated**: The generator sets this to the current timestamp each time it runs, providing a record of when the metadata was last refreshed. No configuration needed.

**feedbackMechanism**: The repository's issues URL in the format of {repositoryURL}/issues. If you already have a code.json file with existing feedback mechanisms, the generator preserves those values. No configuration needed.

Expand Down Expand Up @@ -213,6 +239,7 @@ An up-to-date list of core team members can be found in [MAINTAINERS.md](MAINTAI
.
├── src/
│ ├── model.ts # TypeScript interfaces for code.json schema
│ ├── validation.ts # Zod schema definitions and validation logic
│ ├── main.ts # Main action logic
│ ├── helper.ts # Helper functions for GitHub API interactions
│ └── index.ts # Action entrypoint
Expand Down
35 changes: 9 additions & 26 deletions code.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,10 @@
"forks": 2,
"clones": 0
},
"platforms": [
"web",
"linux"
],
"categories": [
"developer-tools",
"automation"
],
"platforms": ["web", "linux"],
"categories": ["developer-tools", "automation"],
"softwareType": "standalone/backend",
"languages": [
"TypeScript",
"JavaScript"
],
"languages": ["TypeScript", "JavaScript"],
"maintenance": "internal",
"contractNumber": [],
"SBOM": "https://github.com/DSACMS/automated-codejson-generator/network/dependencies",
Expand All @@ -50,11 +41,9 @@
"date": {
"created": "2025-02-07T16:29:38Z",
"lastModified": "2025-09-17T22:40:02Z",
"metaDataLastUpdated": "2025-09-26T15:08:24.592Z"
"metadataLastUpdated": "2025-09-26T15:08:24.592Z"
},
"tags": [
"cmsoss-tier2"
],
"tags": ["cmsoss-tier2"],
"contact": {
"email": "opensource@cms.hhs.gov",
"name": "CMS Open Source Team"
Expand All @@ -66,15 +55,9 @@
"userInput": false,
"fismaLevel": "Low",
"group": "CMS/OA/DSAC",
"projects": [
"SHARE IT Act"
],
"projects": ["SHARE IT Act"],
"systems": [],
"subsetInHealthcare": [
"Operational"
],
"userType": [
"Government"
],
"subsetInHealthcare": ["Operational"],
"userType": ["Government"],
"maturityModelTier": 3
}
}
13 changes: 11 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
"dependencies": {
"@actions/core": "^1.11.1",
"@octokit/action": "^7.0.0",
"octokit-plugin-create-pull-request": "^6.0.0"
"octokit-plugin-create-pull-request": "^6.0.0",
"zod": "^4.1.11"
},
"devDependencies": {
"@eslint/compat": "^1.2.6",
Expand Down
36 changes: 34 additions & 2 deletions src/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { exec } from "child_process";
import { promisify } from "util";

import { CodeJSON, BasicRepoInfo } from "./model.js";
import { validateCodeJSON } from "./validation.js";

const execAsync = promisify(exec);

Expand Down Expand Up @@ -66,7 +67,7 @@ export async function calculateMetaData(): Promise<Partial<CodeJSON>> {
date: {
created: basicInfo.date.created,
lastModified: basicInfo.date.lastModified,
metaDataLastUpdated: basicInfo.date.metaDataLastUpdated,
metadataLastUpdated: basicInfo.date.metadataLastUpdated,
},
};
} catch (error) {
Expand Down Expand Up @@ -99,7 +100,7 @@ async function getBasicInfo(): Promise<BasicRepoInfo> {
date: {
created: repoData.data.created_at,
lastModified: repoData.data.updated_at,
metaDataLastUpdated: new Date().toISOString(),
metadataLastUpdated: new Date().toISOString(),
},
};
} catch (error) {
Expand Down Expand Up @@ -139,6 +140,37 @@ export async function getBaseBranch(): Promise<string> {
}
}

//===============================================
// Validation
//===============================================
export async function validateOnly(): Promise<void> {
try {
const codeJSON = await readJSON("/github/workspace/code.json");

if (!codeJSON) {
core.setFailed(
"code.json file not found, is empty, or contains invalid JSON syntax...",
);
return;
}

const validationErrors = validateCodeJSON(codeJSON);

if (validationErrors.length > 0) {
const errorMessage = `code.json validation failed with ${validationErrors.length} error(s):\n\n${validationErrors.map((err, idx) => `${idx + 1}. ${err}`).join("\n")}`;
core.setFailed(errorMessage);
return;
}

core.info("code.json is valid!");
core.setOutput("validated", true);
} catch (error) {
core.setFailed(`validation error: ${error}`);
}
}

export { validateCodeJSON };

//===============================================
// Data Handling
//===============================================
Expand Down
Loading