Skip to content

[Manual Test] {{ test_id }} by {{ user_name }} #56

[Manual Test] {{ test_id }} by {{ user_name }}

[Manual Test] {{ test_id }} by {{ user_name }} #56

Workflow file for this run

name: Manual Test Report
on:
issues:
types: [opened, edited] # run on new issues and on later edits
permissions:
contents: read
issues: write
pull-requests: write
jobs:
process-manual-test:
if: contains(github.event.issue.title, '[Manual Test]')
runs-on: ubuntu-latest
steps:
#--------------------------------------------------------------------
# 1) Check out repository so the issue‑form template is available
#--------------------------------------------------------------------
- name: Checkout repository
uses: actions/checkout@v3
with: { fetch-depth: 1 }
#--------------------------------------------------------------------
# 2) Parse the issue‑form submission
#--------------------------------------------------------------------
- name: Parse issue form
id: parse
uses: issue-ops/[email protected]
with:
body: ${{ github.event.issue.body }}
issue-form-template: manual-test-report.yml # .github/ISSUE_TEMPLATE/
workspace: ${{ github.workspace }}
#--------------------------------------------------------------------
# 3) Gather all data for the project fields
#--------------------------------------------------------------------
- name: Build field set
id: build
run: |
{
echo "test_id=${{ steps.parse.outputs.numeric_test_id }}"
echo "estimate=${{ steps.parse.outputs.estimate_text }}"
echo "lv_version=${{ steps.parse.outputs.labview_version }}"
echo "bitness=${{ steps.parse.outputs.labview_bitness }}"
echo "os_used=${{ steps.parse.outputs.os_used }}"
echo "test_result=${{ steps.parse.outputs.test_result }}"
echo "notes=${{ steps.parse.outputs.notes }}"
} >> "$GITHUB_OUTPUT"
#--------------------------------------------------------------------
# 4) Update / create project fields in Org‑level Project #29
#--------------------------------------------------------------------
- name: Update project fields
uses: actions/github-script@v6
env:
ORG_LOGIN: ni # << organisation login
PROJ_NUMBER: 29 # << project number from the URL
TEST_ID: ${{ steps.build.outputs.test_id }}
ESTIMATE: ${{ steps.build.outputs.estimate }}
LV_VERSION: ${{ steps.build.outputs.lv_version }}
BITNESS: ${{ steps.build.outputs.bitness }}
OS_USED: ${{ steps.build.outputs.os_used }}
TEST_RESULT: ${{ steps.build.outputs.test_result }}
NOTES: ${{ steps.build.outputs.notes }}
with:
github-token: ${{ secrets.PROJECTS_PAT }} # PAT with “Projects RW” & “Issues R”
script: |
const orgLogin = process.env.ORG_LOGIN;
const projectNumber= parseInt(process.env.PROJ_NUMBER, 10);
const contentId = context.payload.issue.node_id;
//----------------------------------------------------------------
// Helper logging
//----------------------------------------------------------------
const log = (msg) => core.info(` ${msg}`);
const warn = (msg) => core.warning(` ${msg}`);
//----------------------------------------------------------------
// 1) Resolve the project node‑ID dynamically
//----------------------------------------------------------------
log(`🔍 Resolving Project #${projectNumber} in org “${orgLogin}”`);
const projRes = await github.graphql(`
query ($org:String!, $num:Int!){
organization(login:$org){
projectV2(number:$num){ id title }
}
}`, { org: orgLogin, num: projectNumber });
const project = projRes.organization.projectV2;
if (!project) core.setFailed(`Project #${projectNumber} not found in “${orgLogin}”.`);
const projectId = project.id;
log(`➡️ Using project “${project.title}” (ID: ${projectId})`);
//----------------------------------------------------------------
// 2) Ensure a project item exists for this issue
//----------------------------------------------------------------
async function ensureProjectItem () {
const itemsRes = await github.graphql(`
query ($pid:ID!, $after:String){
node(id:$pid){
... on ProjectV2 {
items(first:100, after:$after){
nodes { id content { ... on Issue { id } } }
pageInfo { hasNextPage endCursor }
}
}
}
}`, { pid: projectId });
let itemId =
itemsRes.node.items.nodes.find(n => n.content?.id === contentId)?.id;
// If not found (or >100 items), keep paginating
let cursor = itemsRes.node.items.pageInfo.endCursor;
let hasMore= itemsRes.node.items.pageInfo.hasNextPage;
while (!itemId && hasMore) {
const moreRes = await github.graphql(/* GraphQL */`
query ($pid:ID!, $after:String){
node(id:$pid){
... on ProjectV2 {
items(first:100, after:$after){
nodes { id content { ... on Issue { id } } }
pageInfo { hasNextPage endCursor }
}
}
}
}`, { pid: projectId, after: cursor });
itemId =
moreRes.node.items.nodes.find(n => n.content?.id === contentId)?.id;
cursor = moreRes.node.items.pageInfo.endCursor;
hasMore = moreRes.node.items.pageInfo.hasNextPage;
}
if (itemId) {
log(`ℹ️ Found existing project item ${itemId}`);
return itemId;
}
log('➕ Adding issue to project…');
const add = await github.graphql(`
mutation ($pid:ID!, $cid:ID!){
addProjectV2ItemById(input:{ projectId:$pid, contentId:$cid }){
item { id }
}
}`, { pid: projectId, cid: contentId });
return add.addProjectV2ItemById.item.id;
}
//----------------------------------------------------------------
// 3) Fetch project fields – create missing ones
//----------------------------------------------------------------
async function fetchFields () {
const res = await github.graphql(`
query ($pid:ID!){
node(id:$pid){
... on ProjectV2 {
fields(first:100){
nodes {
id name dataType
... on ProjectV2SingleSelectField { options { id name } }
}
}
}
}
}`, { pid: projectId });
return res.node.fields.nodes;
}
async function createField (name, type) {
log(`⚙️ Creating field “${name}”`);
const res = await github.graphql(`
mutation ($pid:ID!, $name:String!, $type:ProjectV2CustomFieldDataType!){
createProjectV2Field(input:{
projectId:$pid, name:$name, dataType:$type,
${type === 'SINGLE_SELECT'
? 'singleSelectOptions:[{name:"Passed"},{name:"Failed"},{name:"Needs Review"}]'
: ''}
}){
field {
id name dataType
... on ProjectV2SingleSelectField { options { id name } }
}
}
}`, { pid: projectId, name, type });
return res.createProjectV2Field.field;
}
async function ensureOption (field, optionName){
const opt = field.options.find(o => o.name === optionName);
if (opt) return opt.id;
log(`⚙️ Adding option “${optionName}” to “${field.name}”`);
const res = await github.graphql(`
mutation ($pid:ID!, $fid:ID!, $name:String!){
createProjectV2SingleSelectFieldOption(input:{
projectId:$pid, fieldId:$fid, name:$name
}){
option { id }
}
}`, { pid: projectId, fid: field.id, name: optionName });
return res.createProjectV2SingleSelectFieldOption.option.id;
}
async function setField (itemId, field, value){
let payload;
if (field.dataType === 'SINGLE_SELECT'){
const optId = await ensureOption(field, value);
payload = { singleSelectOptionId: optId };
} else {
payload = { text: value };
}
await github.graphql(`
mutation ($pid:ID!, $iid:ID!, $fid:ID!, $val:ProjectV2FieldValue!){
updateProjectV2ItemFieldValue(input:{
projectId:$pid, itemId:$iid, fieldId:$fid, value:$val
}) { projectV2Item { id } }
}`, { pid: projectId, iid: itemId, fid: field.id, val: payload });
log(`✅ “${field.name}” → “${value}”`);
}
//----------------------------------------------------------------
// 4) Main execution
//----------------------------------------------------------------
const itemId = await ensureProjectItem();
const fields = await fetchFields();
const entries = [
{ n:'TestID', v:process.env.TEST_ID, t:'TEXT' },
{ n:'Estimate', v:process.env.ESTIMATE, t:'TEXT' },
{ n:'Operating System', v:process.env.OS_USED, t:'TEXT' },
{ n:'LabVIEW Version', v:process.env.LV_VERSION, t:'TEXT' },
{ n:'LabVIEW Bitness', v:process.env.BITNESS, t:'TEXT' },
{ n:'Notes', v:process.env.NOTES, t:'TEXT' },
{ n:'Test Result', v:process.env.TEST_RESULT, t:'SINGLE_SELECT' }
];
log('✏️ Updating project fields');
for (const {n,v,t} of entries){
if (!v){ warn(`Skipping “${n}” – empty value`); continue; }
let fld = fields.find(f => f.name === n);
if (!fld){
fld = await createField(n, t);
fields.push(fld);
}
await setField(itemId, fld, v);
}
log('🎉 Project update complete');