Internal promotion

When you release a course, promote it internally so the right teams and stakeholders know it is live and can share it with learners.

In this lesson, you will learn how to:

  • Promote a release to the right internal teams

  • Post course metadata to Asana automatically on merge

  • Send a Slack promo when a PR with the Release label is merged

  • Request SME feedback on Slack when the sme-review label is applied

  • Find and delete messages posted by the bot

Where to promote

Plan where to announce each release so the right people see it:

  • Asana – Create tasks on the right boards so marketing, content, and product can track and act. The repo can post course metadata to Asana automatically when a labelled PR is merged; the next section explains how to configure it.

  • Slack – Post in #graphacademy and any other channels that care about new or updated courses.

  • Team and SME channels – Notify subject-matter experts and relevant team channels so they can share the course with their audiences.

Writing blog posts for promotion

Include writing a blog post in your promotion plan. A short post on the Neo4j blog or developer blog helps learners discover the course and supports discoverability.

Posting to Asana

When a PR is merged with a label such as Release, Draft, or Enhancement, a GitHub Actions workflow can post course metadata to Asana so the right boards and assignees get a task.

How it works

The workflow:

  • Finds which course.adoc files were changed in the PR

  • Derives the course slug(s) (e.g. aura-agents)

  • Loads the matching config file from config/promo/ (e.g. release.json for the Release label)

  • For each destination in that config, creates an Asana task with the course title, caption, link, key points, and categories, and assigns it to the right board, section, and assignee

Config files

Destination boards, assignees, and Slack channels are defined in combined JSON config files in config/promo/. Each label type can have its own config:

  • release.json – used when the Release label is merged

  • draft.json – for a Draft label (create one if needed)

  • enhancement.json – for an Enhancement label (create one if needed)

The workflow chooses the file from the merged PR’s label and the ASANA_CONFIG / SLACK_CONFIG variables (default release). The same file contains asana.destinations and slack.channels.

Each entry in asana.destinations has:

  • project – Asana project (board) GID

  • section – optional section (column) GID

  • assignee – optional user GID

  • name – optional label for that destination (e.g. "GraphAcademy Marketing")

Example (config/promo/release.json):

json
{
  "asana": {
    "destinations": [
      {
        "name": "GraphAcademy Marketing",
        "project": "1208974749704120",
        "section": "1208974749704126",
        "assignee": "1202055636798551"
      }
    ]
  },
  "slack": {
    "channels": ["graphacademy"]
  }
}

Tasks are created with a due date two weeks from the day they are posted.

Finding GIDs

You need three GIDs per destination: project (board), section (column), and assignee (person). Set ASANA_API_KEY in .env (or the environment) before running any of the commands below.

Easiest: build a destination from the board URL

If you have the Asana board URL and know the section name and assignee name, use the combined command. It looks up all GIDs and prints the destination JSON ready to paste into config/promo/release.json (under asana.destinations):

sh
npm run asana:url-to-destination -- \
  --url "https://app.asana.com/1/200109678723468/project/1211853693432549/list/1211854278859853" \
  --section "Marquee Assets in Production" \
  --assignee "Greg Posten"
  • --url – the full Asana project/list URL (the project GID is read from it)

  • --section – the exact or partial section name (e.g. column name)

  • --assignee – the exact or partial person name (or email)

  • --name – optional label for this destination in the config

The script prints the project name and GID, the section and assignee (or lists options if a name does not match), then the destination object. Copy the JSON into the destinations array in your config file.

Project GID from the URL

The project GID is the number that appears after /project/ in the board URL.

Example: in https://app.asana.com/1/200109678723468/project/1211853693432549/list/…​; the project GID is 1211853693432549.

The /list/ number in the URL is not the section GID; use the commands below to get the real section GID.

List sections for a board

To get the section GID for a column (e.g. "Marketing & Outreach", "Marquee Assets in Production"), list sections for the project:

sh
npm run asana:list-sections -- <project_gid>

The output shows each section name and its GID. Use the GID in the section field of your destination.

List users (assignees)

To get a user GID for the assignee field, list users in the project’s workspace. You can filter by name or email:

sh
npm run asana:list-assignees -- --project <project_gid>
npm run asana:list-assignees -- --project <project_gid> --search "Greg"

Use the GID of the right user as assignee in your destination.

Running locally

To post metadata for a course to Asana without merging a PR, run:

sh
ASANA_CONFIG=release npm run release:asana -- <course_slug>

Example:

sh
ASANA_CONFIG=release npm run release:asana -- aura-agents

Use ASANA_CONFIG to choose the config file (e.g. release, draft, config.example). Ensure ASANA_API_KEY is set (e.g. in .env).

Workflow summary

  • Trigger: PR merged with a label (e.g. Release)

  • Config: config/promo/{ASANA_CONFIG}.json (e.g. release.json), section asana.destinations

  • Metadata: Read from each merged course’s course.adoc (title, caption, key points, categories); link is built from the course slug

  • Output: One Asana task per destination, with due date in two weeks

Posting to Slack

The same Release workflow posts a promo message to Slack. The message is built from an AsciiDoc template in which course attributes are replaced, then sent to each channel in the config.

Template and config

  • Templateasciidoc/shared/release/{SLACK_CONFIG}.adoc (e.g. release.adoc). The title is merged from the course document’s getTitle() (available in the template as Internal promotion); other attributes are {caption}, {link}, {key-points}, {categories}. The template is rendered by the same AsciiDoc processor used elsewhere in the repo; the output is then converted to Slack-friendly text.

  • Config – The same file config/promo/release.json has a slack.channels array: channel names (e.g. graphacademy) or channel IDs. The workflow uses SLACK_CONFIG=release and the secret SLACK_BOT_TOKEN (Bot User OAuth Token with chat:write).

Running locally

To post the Slack promo for a course without merging a PR:

sh
SLACK_CONFIG=release npm run release:slack -- <course_slug>

Ensure SLACK_BOT_TOKEN is set (e.g. in .env). The same template and config are used as in the workflow.

Managing Slack messages

If you need to find or remove a message the bot posted, two commands are available. Both require SLACK_BOT_TOKEN set in .env.

Pass the channel ID, not the channel name. Find it in Slack by right-clicking the channel and selecting View channel details — the ID appears at the bottom of that dialog.

Listing bot messages

List the most recent messages the bot posted in a channel:

sh
npm run slack:list-messages -- --channel <channel_ID>

Use --limit to fetch more messages, and --search to filter by keyword:

sh
npm run slack:list-messages -- --channel <channel_ID> --limit 20 --search "aura-agents"

The output shows a Timestamp column. Copy that value to delete a specific message.

Deleting a bot message

sh
npm run slack:delete-message -- --channel <channel_ID> --ts <timestamp>

Use --dry-run to confirm what would be deleted before committing:

sh
npm run slack:delete-message -- --channel <channel_ID> --ts <timestamp> --dry-run

Requesting SME review

Before merging a PR, you can request subject-matter expert feedback by applying the sme-review label to the PR. A GitHub Actions workflow detects the label and posts a review request to the #graphacademy Slack channel.

The message includes the course title, caption, and a link to the course on GraphAcademy, and asks reviewers to share corrections or suggestions in thread.

How it works

  • Applying the sme-review label triggers the sme-review-slack.yml workflow

  • The workflow finds which course.adoc files are in the PR and derives the course slug(s)

  • It posts using the template asciidoc/shared/release/sme-review.adoc and sends the message to the channels defined in config/promo/sme-review.json

Edit asciidoc/shared/release/sme-review.adoc to change the message wording, and edit config/promo/sme-review.json to add or remove channels.

Running locally

To send an SME review request for a course without applying a label to a PR:

sh
npm run review:slack -- <course_slug>

Example:

sh
npm run review:slack -- aura-agents

Ensure SLACK_BOT_TOKEN is set (e.g. in .env).

Summary

In this lesson, you learned how to promote a release internally and request pre-release feedback using GitHub PR labels.

  • Asana – Merging a PR with the Release label posts course metadata to the boards and assignees configured in config/promo/release.json (asana.destinations). Use asana:url-to-destination, asana:list-sections, and asana:list-assignees to build destination entries.

  • Slack release promo – The same merge posts to the channels in config/promo/release.json (slack.channels) using the template at asciidoc/shared/release/release.adoc. Run release:slack <slug> locally to test.

  • SME review request – Applying the sme-review label to an open PR posts a feedback request to the channels in config/promo/sme-review.json using the template at asciidoc/shared/release/sme-review.adoc. Run review:slack <slug> locally to test.

  • Managing bot messages – Use slack:list-messages --channel <ID> to find recent bot posts and slack:delete-message --channel <ID> --ts <timestamp> to remove one. Pass the channel ID (not the name); find it in Slack by right-clicking the channel and selecting View channel details.

Chatbot

How can I help you today?

Data Model

Your data model will appear here.